summaryrefslogtreecommitdiff
path: root/design-documents
diff options
context:
space:
mode:
Diffstat (limited to 'design-documents')
-rw-r--r--design-documents/001-new-browser-integration.rst4
-rw-r--r--design-documents/002-wallet-exchange-management.rst6
-rw-r--r--design-documents/003-tos-rendering.rst4
-rw-r--r--design-documents/004-wallet-withdrawal-flow.rst4
-rw-r--r--design-documents/005-wallet-backup-sync.rst8
-rw-r--r--design-documents/006-extensions.rst174
-rw-r--r--design-documents/007-payment.rst4
-rw-r--r--design-documents/008-fees.rst4
-rw-r--r--design-documents/009-backup.rst4
-rw-r--r--design-documents/010-exchange-helpers.rst10
-rw-r--r--design-documents/011-auditor-db-sync.rst14
-rw-r--r--design-documents/012-fee-schedule-metrics.rst8
-rw-r--r--design-documents/013-peer-to-peer-payments.rst39
-rw-r--r--design-documents/014-merchant-backoffice-ui.rst4
-rw-r--r--design-documents/015-merchant-backoffice-routing.rst29
-rw-r--r--design-documents/016-backoffice-order-management.rst8
-rw-r--r--design-documents/017-backoffice-inventory-management.rst20
-rw-r--r--design-documents/018-contract-json.rst8
-rw-r--r--design-documents/019-wallet-backup-merge.rst4
-rw-r--r--design-documents/020-backoffice-tips-management.rst6
-rw-r--r--design-documents/021-exchange-key-continuity.rst4
-rw-r--r--design-documents/023-taler-kyc.rst497
-rw-r--r--design-documents/024-age-restriction.rst769
-rw-r--r--design-documents/025-withdraw-from-wallet.rst66
-rw-r--r--design-documents/026-refund-fees.rst61
-rw-r--r--design-documents/027-sandboxing-taler.rst165
-rw-r--r--design-documents/028-deposit-policies.rst188
-rw-r--r--design-documents/029-mobile-ui.rst41
-rw-r--r--design-documents/030-offline-payments.rst99
-rw-r--r--design-documents/031-invoicing.rst202
-rw-r--r--design-documents/032-brandt-vickrey-auctions.rst312
-rw-r--r--design-documents/033-database.rst152
-rw-r--r--design-documents/_svgs/escrow-flow.svg1
-rw-r--r--design-documents/index.rst9
34 files changed, 2600 insertions, 328 deletions
diff --git a/design-documents/001-new-browser-integration.rst b/design-documents/001-new-browser-integration.rst
index 8dbe2a43..80a08859 100644
--- a/design-documents/001-new-browser-integration.rst
+++ b/design-documents/001-new-browser-integration.rst
@@ -1,5 +1,5 @@
-Design Doc 001: New Browser Integration
-#######################################
+DD1: New Browser Integration
+############################
.. warning::
diff --git a/design-documents/002-wallet-exchange-management.rst b/design-documents/002-wallet-exchange-management.rst
index 9d10045a..297c23f8 100644
--- a/design-documents/002-wallet-exchange-management.rst
+++ b/design-documents/002-wallet-exchange-management.rst
@@ -1,5 +1,5 @@
-Design Doc 002: Wallet Exchange Management
-##########################################
+DD2: Wallet Exchange Management
+###############################
.. note::
@@ -40,7 +40,7 @@ audited by a trusted auditor.
An exchange might only be known the wallet temporarily. For example,
the wallet UI may allow the user to review the fee structure of an
exchange before the wallet is permanently added to the wallet.
-Once a an exchange is either (a) marked as trusted or (b) used for a
+Once an exchange is either (a) marked as trusted or (b) used for a
withdrawal operation, it is marked as permanent.
Exchanges that are not permanent will be automatically be removed
diff --git a/design-documents/003-tos-rendering.rst b/design-documents/003-tos-rendering.rst
index 3011775c..9b19ee02 100644
--- a/design-documents/003-tos-rendering.rst
+++ b/design-documents/003-tos-rendering.rst
@@ -1,5 +1,5 @@
-Design Doc 003: ToS rendering
-#############################
+DD3: ToS rendering
+##################
Summary
=======
diff --git a/design-documents/004-wallet-withdrawal-flow.rst b/design-documents/004-wallet-withdrawal-flow.rst
index 28e9c16b..a385959b 100644
--- a/design-documents/004-wallet-withdrawal-flow.rst
+++ b/design-documents/004-wallet-withdrawal-flow.rst
@@ -1,5 +1,5 @@
-Design Doc 004: Wallet Withdrawal Flow
-######################################
+DD4: Wallet Withdrawal Flow
+###########################
Summary
=======
diff --git a/design-documents/005-wallet-backup-sync.rst b/design-documents/005-wallet-backup-sync.rst
index 20fce37b..7592242c 100644
--- a/design-documents/005-wallet-backup-sync.rst
+++ b/design-documents/005-wallet-backup-sync.rst
@@ -1,5 +1,5 @@
-Design Doc 005: Wallet Backup and Sync
-######################################
+DD5: Wallet Backup and Sync
+###########################
.. warning::
@@ -56,13 +56,13 @@ Entities that are **not** synchronized are:
* withdrawal operations before they have been accepted by the user
Entities that **could** be synchronized (to be decided):
-
+
* private keys of other sync accounts
* coin planchets
* tips before the corresponding coins have been withdrawn
* refresh sessions (not only the "meta data" about the operation,
but everything)
-
+
Garbage collection
------------------
diff --git a/design-documents/006-extensions.rst b/design-documents/006-extensions.rst
index e663b2f5..9dde065d 100644
--- a/design-documents/006-extensions.rst
+++ b/design-documents/006-extensions.rst
@@ -1,5 +1,5 @@
-Design Document 006: Extensions for GNU Taler
-#############################################
+DD6: Extensions for GNU Taler
+#############################
Summary
=======
@@ -18,6 +18,7 @@ of 2021 and 2022:
* Peer-to-peer payments
* Anonymous age-restriction
* Escrow service for anonymous auctions
+* A general escrow service
We call a feature an *extension* when it is *optional* for either the
exchange, wallet or merchant to enable and support it. (However, enabling
@@ -31,9 +32,6 @@ participants.
Requirements
============
-TODO. Not sure if we have any requirements - other than particular
-ideas/designs for extensions?
-
Proposed Solution
=================
@@ -41,70 +39,151 @@ Proposed Solution
Exchange
^^^^^^^^
-The exchange will add two new REQUIRED fields in response to ``/keys``:
+The exchange will add two new *optional* fields in response to ``/keys``:
-#. The (but maybe empty) field ``extensions`` which contains a dictionary of
+#. The field ``extensions`` which contains a dictionary of
extension-names and their configuration, see below.
-#. The field ``extensions_sig`` that contains the EdDSA signature of the SHA256-hash
- of the normalized JSON-string of the ``extenstions`` object.
-
-The names of extensions MUST be unique and SHOULD include a version information
-in Taler's `protocol version ranges notation`_ as suffix starting with letter
-'``v``', f.e.: ``age_restriction.v1`` or ``p2p.v1:2:3``.
+#. The field ``extensions_sig`` that contains the EdDSA signature of the
+ SHA256-hash of the normalized JSON-string of the ``extensions`` object.
-.. _protocol version ranges notation: https://docs.taler.net/core/api-common.html#protocol-version-ranges
+The necessary changes to ``ExtensionsManifestsResponse`` are highlighted here:
-The necessary changes to ``ExchangeKeysResponse`` are highlighted here:
+.. ts:def:: ExtensionsManifestsResponse
-.. ts:def:: ExchangeKeysResponse
-
- interface ExchangeKeysResponse {
+ interface ExtensionsManifestsResponse {
//...
- // Required (but maybe emtpy) field with a dictionary of (name, object)
- // pairs defining the supported extensions.
- // The name MUST be unique and SHOULD include version information in Taler's
- // protocol version ranges notation as suffix, starting with letter 'v',
- // f.e.: "age_restriction.v1" or "p2p.v1:2:3".
- extensions: { name: Extension };
+ // Optional field with a dictionary of (name, object) pairs defining the
+ // supported and enabled extensions.
+ // The name MUST be non-empty and unique.
+ extensions?: { name: ExtensionManifest };
- // Signature by the exchange master key of the SHA-512 hash of the
- // normalized JSON-object of field ``extenstions``.
+ // Signature by the exchange master key of the SHA-256 hash of the
+ // normalized JSON-object of field ``extensions``, if it was set.
// The signature MUST have purpose ``TALER_SIGNATURE_MASTER_EXTENSIONS``.
- extensions_sig: EddsaSignature;
+ extensions_sig?: EddsaSignature;
//...
}
-The definition of ``Extension`` object itself is mostly up to the particular
-feature. However, it MUST contain the following fields:
+Extension names
+---------------
+
+The names of extensions MUST be unique. The full name MUST be registered with
+GANA_ along with a full description of the extension.
+
+.. _GANA: https://git.gnunet.org/gana.git
+
+(In the rare situation that the exchange might have to provide *multiple*
+versions of the "same" feature in parallel, multiple unique names MUST be used,
+f.e. ``age_restriction`` an ``age_restriction.v2``.)
+
+ExtensionManifest object
+---------------------------
+
+The definition of ``ExtensionManifest`` object itself is mostly up to the
+particular feature. **However**, it MUST have
-* ``description`` ― a short description of the feature itself. Can be used by wallets to display information about the feature to the customer.
+#. the boolean field ``critical`` that has the same semantics as as "critical"
+ has for extensions in X.509_: if true, the client must "understand" the
+ extension before proceeding, if "false" clients can safely skip extensions
+ they do not understand.
-* ``required`` ― a boolean that indicates if this feature MUST be supported by the wallets and/or merchants in order to use this exchange.
+#. the field ``version`` of type `LibtoolVersion` which contains the version
+ information of the extension in Taler's `protocol version ranges notation`_.
-.. ts:def:: Extension
+.. _X.509: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2
- interface Extension {
- // Short description of the feature.
- description: string;
+.. _`protocol version ranges notation`: https://docs.taler.net/core/api-common.html#protocol-version-ranges
- // Set to ``true`` if this extension MUST be supported by wallets and/or
- // merchants.
- required: boolean;
+.. ts:def:: ExtensionManifest
- // Additional fields defined by the feature itself
- ...
+ interface ExtensionManifest {
+ // The criticality of the extension MUST be provided. It has the same
+ // semantics as "critical" has for extensions in X.509:
+ // - if "true", the client must "understand" the extension before
+ // proceeding,
+ // - if "false", clients can safely skip extensions they do not
+ // understand.
+ // (see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2)
+ critical: boolean;
+ // The version information MUST be provided in Taler's protocol version
+ // ranges notation, see
+ // https://docs.taler.net/core/api-common.html#protocol-version-ranges
+ version: LibtoolVersion;
+
+ // Optional configuration object, defined by the feature itself
+ config?: object;
}
-**TODO**:
+Configuration
+-------------
+
+Extensions are *disabled* per default and must *explicetly* be enabled in the
+the TALER configuration manually. The configurations of all enabled extensions
+are signed with the master key and uploaded to the exchange with the tool
+``taler-exchange-offline``.
+
+Each extension has its own section in the configuration, starting with the
+prefix ``exchange-extension-``, like ``[exchange-extension-age_restriction]``.
+The field ``ENABLED = YES|NO`` is used to enable or disable the corresponding
+extension. If the extension has its own configuration parameters, they MAY be
+optional, in which case the ``taler-exchange-offline`` tool MUST fill them with
+safe default values.
+
+The ``taler-exchange-offline`` tool MUST offer the subcommand ``extensions``
+for showing and signing extensions. For this purpose, the following
+sub-subcommands MUST be available:
+
+* ``extensions show``: List all available extensions, their versions,
+ criticality and whether they are enabled.
+* ``extensions sign``: Sign the configuration of all enabled extensions with
+ the master key and prepare a JSON-object for the ``upload`` command.
+
+When extensions are offered and enabled by an exchange, the ``extensions``
+object MUST be signed by the exchange's master signing key. Whenever
+extensions are enabled or disabled, the offline tool MUST sign the SHA256 hash
+of the normalized JSON-string of the ``extensions`` object, if it is not empty.
+
+In order to do so, the ``taler-exchange-offline`` tool MUST
+
+#. have the complete list of all available optional features/extensions and
+ their versions builtin and
+
+#. understand them (including the version). For example, the extension for
+ age-restriction will require the exchange to perform particular steps when
+ this extension is enabled (i.e. signing denominations with support with age
+ restriction *together* with the string of age groups).
+
+#. reject a configuration that refers to any extension that the tool does not
+ know or understand.
+
+Similarly, the exchange MUST reject a signed configuration with extensions it
+does not know or understand.
+
+Examples
+--------
+
+A configuration for age-restriction in the taler configuration would look like
+this:
+
+.. code:: none
+
+ [exchange-extension-age_restriction]
+ ENABLED = true
+ # default:
+ AGE_GROUPS = "8:10:12:14:16:18:21"
+
+
+ [exchange-extension-policy_brandt_vickery_auction]
+ ENABLED = true
+ REPLAY_PROGRAM = "/usr/local/bin/taler-exchange-auction_replay"
-* Add examples for age-restriction and p2p.
Merchant
^^^^^^^^
@@ -113,6 +192,16 @@ TODO:
* Needs to express support for particular extensions, too. F.e. age-restriction.
+Extension Plugins
+==================
+
+TODO:
+
+* describe ``struct TALER_Extension``
+* describe the plugin loading mechanism for extensions
+* describe the various handlers
+
+
Alternatives
============
@@ -132,4 +221,3 @@ Discussion / Q&A
The initial ideas presented here are based on discussions between Özgür Kesim
and Christian Grothoff.
-
diff --git a/design-documents/007-payment.rst b/design-documents/007-payment.rst
index 115ac3f8..2750c816 100644
--- a/design-documents/007-payment.rst
+++ b/design-documents/007-payment.rst
@@ -1,5 +1,5 @@
-Design Doc 007: Specification of the Payment Flow
-#################################################
+DD7: Specification of the Payment Flow
+######################################
Summary
=======
diff --git a/design-documents/008-fees.rst b/design-documents/008-fees.rst
index 47c2af8a..b99190c4 100644
--- a/design-documents/008-fees.rst
+++ b/design-documents/008-fees.rst
@@ -1,5 +1,5 @@
-Design Doc 008: Fee Structure Metrics
-#####################################
+DD8: Fee Structure Metrics
+##########################
.. note::
diff --git a/design-documents/009-backup.rst b/design-documents/009-backup.rst
index 04ae628c..1cf9bb23 100644
--- a/design-documents/009-backup.rst
+++ b/design-documents/009-backup.rst
@@ -1,5 +1,5 @@
-Design Doc 009: Wallet Backup
-#############################
+DD9: Wallet Backup
+##################
Summary
=======
diff --git a/design-documents/010-exchange-helpers.rst b/design-documents/010-exchange-helpers.rst
index e67fca4e..fe9de063 100644
--- a/design-documents/010-exchange-helpers.rst
+++ b/design-documents/010-exchange-helpers.rst
@@ -1,5 +1,5 @@
-Design Doc 010: Exchange crypto helper design
-#############################################
+DD10: Exchange crypto helper design
+###################################
Summary
=======
@@ -13,7 +13,7 @@ Motivation
We want to provide an additional layer of protection for the private online
signing keys used by the exchange. The exchange is network-facing, includes an
-HTTP server, Postgres interaction, JSON parser and quite a bit of other logic
+HTTP server, PostgreSQL interaction, JSON parser and quite a bit of other logic
which may all be theoretically vulnerable to remote exploitation. Thus, it
would be good from a security perspective to protect the private online
signing keys via an additional layer of protection.
@@ -89,7 +89,7 @@ Exchange design considerations:
exchange. This simplifies the exchange, and we already needed the
exchange operator to start four processes to operate an exchange.
So this number simply increases to six (not even counting the
- Postgres database and a reverse HTTP proxy for TLS termination).
+ PostgreSQL database and a reverse HTTP proxy for TLS termination).
* Each exchange thread will create its own connection to the helpers, and will
block while waiting on the helper to create a signature. This keeps the
exchange logic simple and similar to the existing in-line signing calls.
@@ -108,7 +108,7 @@ New exchange endpoints:
sign based on that file, and then upload the resulting signature back to
the exchange. For this, master signatures will be POSTed to
the exchange to the ``/keys`` endpoint.
- The exchange will keep those signatures in the Postgres database.
+ The exchange will keep those signatures in the PostgreSQL database.
* A new endpoint (``/auditors``) will also allow adding/removing auditors
(POST, DELETE) using requests signed with the offline master private key.
Once an auditor has been added, the respective auditor signatures on exchange
diff --git a/design-documents/011-auditor-db-sync.rst b/design-documents/011-auditor-db-sync.rst
index fb2e3bea..8a460ed8 100644
--- a/design-documents/011-auditor-db-sync.rst
+++ b/design-documents/011-auditor-db-sync.rst
@@ -1,5 +1,5 @@
-Design Doc 011: Auditor-Exchange Database Synchronization
-#########################################################
+DD11: Auditor-Exchange Database Synchronization
+###############################################
Summary
=======
@@ -62,7 +62,7 @@ Proposed Solution
=================
* Use "common" incremental database replication (whichever is
- approproate for the exchange database setup, synchronous
+ appropriate for the exchange database setup, synchronous
or asynchronous) to make a 1:1 copy of the exchange database
at the auditor. This should work for any full-featured
modern database. This "ingress" copy cannot be trusted, as constraint
@@ -89,9 +89,9 @@ Proposed Solution
* The auditor's "ingress" database should be well isolated from
the rest of the auditor's system and database
(different user accounts). The reason is that we should not
- assume that the Postgres replication code is battle-tested with
+ assume that the PostgreSQL replication code is battle-tested with
malicious parties in mind.
-* The canonical Postgres synchronization between exchange and the
+* The canonical PostgreSQL synchronization between exchange and the
auditor's "ingress" database must use transport security.
The above solution does not gracefully handle mutable tables on which
@@ -148,10 +148,10 @@ A good order for replicating the tables should be:
Alternatives
============
-* Copy the Postgres WAL, filter it for "illegal" operations
+* Copy the PostgreSQL WAL, filter it for "illegal" operations
and then apply it at the auditor end. Disadvantages: WAL
filtering is not a common operation (format documented?),
- this would be highly Postgres-specific, and would require
+ this would be highly PostgreSQL-specific, and would require
complex work to write the filter. Also unsure how one
could later recover gracefully from transient errors
(say where the exchange recified a bogus DELETE).
diff --git a/design-documents/012-fee-schedule-metrics.rst b/design-documents/012-fee-schedule-metrics.rst
index 8eb36da7..3ab7f565 100644
--- a/design-documents/012-fee-schedule-metrics.rst
+++ b/design-documents/012-fee-schedule-metrics.rst
@@ -1,5 +1,5 @@
-Design Doc 012: Exchange Fee Configuration
-##########################################
+DD12: Exchange Fee Configuration
+################################
.. note::
@@ -466,7 +466,7 @@ Note that for a typical transaction, the number of coins is logarithmic to the
amount. So with the above fee structure, paying amounts around 10 EUR would on
average involve about 6 coins with 1/3rd fees at 0.005, 1/3rd fees at 0.01 and
1/3rd fees at 0.015, resulting in an expected total transaction cost in
-**deposit** fees of 0.03 EUR. In constrast, paying 0.50 cents would require
+**deposit** fees of 0.03 EUR. In contrast, paying 0.50 cents would require
on average 4 coins cost less than 0.02 EUR in **deposit** fees. As a result
of this fee structure, microtransactions with Taler have a higher fee in terms
of percentage, while larger transactions are still highly competitive.
@@ -544,4 +544,4 @@ Other documents regarding fee specifications:
* Fee schedule and metrics from the users' point of view :doc:`008-fees`
-* Wire fee for different wiring methods (``sepa`` or ``x-taler-wire``) <https://docs.taler.net/taler-exchange-manual.html#wire-fee-structure>
+* Wire fee for different wiring methods (``iban`` or ``x-taler-wire``) <https://docs.taler.net/taler-exchange-manual.html#wire-fee-structure>
diff --git a/design-documents/013-peer-to-peer-payments.rst b/design-documents/013-peer-to-peer-payments.rst
index c44ab348..951f7f81 100644
--- a/design-documents/013-peer-to-peer-payments.rst
+++ b/design-documents/013-peer-to-peer-payments.rst
@@ -1,5 +1,5 @@
-Design Doc 013: Wallet-to-Wallet Payments
-#########################################
+DD13: Wallet-to-Wallet Payments
+###############################
Summary
=======
@@ -376,6 +376,8 @@ In this protocol variant, the payer is initiating the process.
3. The payer shares the purse's private key and the base URL
of the exchange where the purse was created with the payee.
This can be done using a ``taler://purse/$BASE_URL/$PURSE_PRIV`` URL.
+ The chapter on ``Refinements`` below clarifies why this
+ step is not quite OK and was modified when implementing the design.
4. The payee uses the new ``/purse/$PURSE_PUB`` endpoint to retrieve
the encrypted contract (if available) and purse balance, which includes all
(coin) deposits and **merges** involving the purse.
@@ -703,7 +705,6 @@ database.)
(merge_request_serial_id BIGSERIAL UNIQUE
,reserve_uuid BYTEA NOT NULL REFERENCES reserves (reserve_uuid) ON DELETE CASCADE
,partner_serial_id INT8 REFERENCES partners(partner_serial_id) ON DELETE CASCADE,
- ,reserve_url TEXT NOT NULL,
,reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32),
,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32),
,reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64))
@@ -721,8 +722,6 @@ database.)
IS 'identifies the reserve';
COMMENT ON COLUMN mergers.partner_serial_id
IS 'identifies the partner exchange, NULL in case the target reserve lives at this exchange';
- COMMENT ON COLUMN mergers.reserve_url
- IS 'payto://-URL of the reserve, identifies the exchange and the reserve';
COMMENT ON COLUMN mergers.reserve_pub
IS 'public key of the target reserve';
COMMENT ON COLUMN mergers.purse_pub
@@ -821,7 +820,6 @@ database.)
CREATE TABLE IF NOT EXISTS purse_deposits
(purse_deposit_serial_id BIGSERIAL UNIQUE
,purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32),
- ,purse_expiration INT8 NOT NULL
,coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE
,amount_with_fee_val INT8 NOT NULL
,amount_with_fee_frac INT4 NOT NULL
@@ -832,8 +830,6 @@ database.)
IS 'Requests depositing coins into a purse';
COMMENT ON COLUMN purse_deposits.purse_pub
IS 'Public key of the purse';
- COMMENT ON COLUMN purse_deposits.purse_expiration
- IS 'When the purse is set to expire';
COMMENT ON COLUMN purse_deposits.coin_pub
IS 'Public key of the coin being deposited';
COMMENT ON COLUMN purse_deposits.amount_with_fee_val
@@ -1072,6 +1068,33 @@ Aside from implementation complexity, the solution has the following drawbacks:
account private key before initiating the KYC process.
+Refinements
+===========
+
+In the original design, a payer making a payment offer sends the purse private
+key to the payee, so that the payee can sign the merge request with it. This
+creates a security issue, as theoretically the payee could sign a different
+contract with the purse private key, and conspire with the exchange to replace
+the original contract. In this case, the payer would be making a payment to
+the "wrong" contract, and have no proof of the exchange an payee conspiring
+against it.
+
+A simple fix seems possible: instead of having simply one public-private key
+pair for a purse, we have a PayerContractKey and a PurseMergeKey pair. The payer
+would pay into a purse identified by the PayerContractKey and associate a
+PurseMergeKey with the purse. The payer can then safely share the
+PayeeMergeKey with the payee, as it is ONLY useful for the merge and not to
+sign the contract. Payments would be made into a purse identified by the
+PurseContractKey.
+
+When payments flow in the other direction, the split of the keys seems
+unnecessary (as only a public key is transmitted anyway. However, schema-wise,
+signing the contract with the PurseContractKey and the merge with the
+PurseMergeKey would still work. Only the public PurseContractKey would need
+to be sent to the payer.
+
+
+
Q / A
=====
diff --git a/design-documents/014-merchant-backoffice-ui.rst b/design-documents/014-merchant-backoffice-ui.rst
index 8c4104dc..63c326cc 100644
--- a/design-documents/014-merchant-backoffice-ui.rst
+++ b/design-documents/014-merchant-backoffice-ui.rst
@@ -1,5 +1,5 @@
-Design Doc 014: Merchant backoffice UI
-######################################
+DD14: Merchant backoffice UI
+############################
Motivation
diff --git a/design-documents/015-merchant-backoffice-routing.rst b/design-documents/015-merchant-backoffice-routing.rst
index f6372dcc..e5110bee 100644
--- a/design-documents/015-merchant-backoffice-routing.rst
+++ b/design-documents/015-merchant-backoffice-routing.rst
@@ -1,21 +1,21 @@
-Design Doc 015: Merchant backoffice Routing
-###########################################
+DD15: Merchant backoffice Routing
+#################################
Motivation
==========
A well defined routing will allow users to share backoffice links pointing
-directly into instance pages (settings, orders, products, etc...)
+directly into instance pages (settings, orders, products, etc...)
-The backoffice should load from the instance URL and then allow a internal
+The backoffice should load from the instance URL and then allow an internal
routing of the views with the possibility to accessing them directly when
sharing a link.
This 3 definitions are going to be use in this document:
-* BACKOFFICE_URL as the url where the app is loaded.
-
+* BACKOFFICE_URL as the url where the app is loaded.
+
* BACKEND_URL as the url where the merchant backend is.
* INSTANCE the name of the instance being manage
@@ -27,13 +27,13 @@ Application Ready definition
The application is considered ready after
* the user tried to login.
-
+
* the application checked that the backend url points to a merchant backend
* the merchant backend response successfully
The backoffice test for ``$BACKEND_URL/config`` to define if the $BACKEND_URL is ok.
-The application can potentially test if the protocol or version matched.
+The application can potentially test if the protocol or version matched.
While the application is not ready, just the top navigation bar will be shown
with a message in the left and the lang selection option.
@@ -58,7 +58,7 @@ Knowing that the $BACKEND_URL points to a correct merchant backend the SPA will
check for ``$BACKEND_URL/management/instances``:
* if Unauthorized ask for credentials
-
+
* if error check with the user
* if not found, then url should end with ``/instances/$INSTANCE``. otherwise is
@@ -69,11 +69,11 @@ check for ``$BACKEND_URL/management/instances``:
When a user access the SPA there are 3 scenarios possible:
* **standard**: admin is false so BACKEND_URL points to a non-default instance.
- standard features and links are shown
+ standard features and links are shown
* **admin**: admin is true so BACKEND_URL point to default instance. same as
before and user can create and list instances with some additional links in
- the sidebar.
+ the sidebar.
* **mimic**: admin is true and the request parameter "instance" is set $INSTANCE
instance. BACKEND_URL point to default instance but the user is managing
@@ -99,7 +99,7 @@ parameter (like order id or product id) it should be accessible from the Sidebar
If the user has admin access, this entry points are available:
- /instances: Show the list of instances currently created
- - /instance/new: Show a instance creation form
+ - /instance/new: Show an instance creation form
Where admin or not, there is also this entry points:
@@ -145,7 +145,7 @@ credentials or the backend url
Not found
---------
-For any case that the backend respond 404 the application will render a
+For any case that the backend respond 404 the application will render a
custom not found page
Default instance is missing
@@ -155,6 +155,3 @@ If the **user is admin** AND is loading the setting page (/update), product list
(/products), order list (/orders) or transfer list (/transfers) AND **gets a
404** it will tell the user that it need to create a default instance before
proceeding.
-
-
-
diff --git a/design-documents/016-backoffice-order-management.rst b/design-documents/016-backoffice-order-management.rst
index 00250cd2..e2fca0fe 100644
--- a/design-documents/016-backoffice-order-management.rst
+++ b/design-documents/016-backoffice-order-management.rst
@@ -1,5 +1,5 @@
-Design Doc 016: Backoffice Order Management
-###########################################
+DD16: Backoffice Order Management
+#################################
Summary
=======
@@ -35,7 +35,7 @@ Listing orders
.. image:: ../backoffice-order-list.svg
:width: 800
-4 tabs will be show for a easy access to common filter, click on any of this and
+4 tabs will be show for an easy access to common filter, click on any of this and
search will reset all filter except date
* paid (default)
@@ -88,7 +88,7 @@ This section has two parts:
The first part will add/remove product from the current stock.
* ``name``: search box to select product by description field. if not found it
- will be a 'create new' option leading to the create product without loosing
+ will be a 'create new' option leading to the create product without losing
context
* ``quantity``: mandatory
diff --git a/design-documents/017-backoffice-inventory-management.rst b/design-documents/017-backoffice-inventory-management.rst
index b2fc6dfd..30a6d835 100644
--- a/design-documents/017-backoffice-inventory-management.rst
+++ b/design-documents/017-backoffice-inventory-management.rst
@@ -1,5 +1,5 @@
-Design Doc 017: Backoffice Inventory Management
-###############################################
+DD17: Backoffice Inventory Management
+#####################################
Summary
=======
@@ -55,7 +55,7 @@ Create and Update Product form
.. image:: ../backoffice-product-create.svg
:width: 800
-
+
Update product will use the same form except for the ``product_id``
* product_id: BACKOFFICE_URL + id
@@ -64,7 +64,7 @@ Update product will use the same form except for the ``product_id``
* name: required, one line
* extra: optional, free text area
-* description localized: list with
+* description localized: list with
* lang: dropdown list with supported lang + custom
* description: text area
@@ -95,7 +95,7 @@ Stock management
* ``never`` button will set next restock to never
-* when updating the product, the option ``without stock`` will no be available
+* when updating the product, the option ``without stock`` will no be available
if the product already has stock
* if the product already exist then:
@@ -119,7 +119,7 @@ Alternatives
============
* price and stock columns in the list can be merged into a more complex column
- with the same information
+ with the same information
* rows in the table can be expandable when clicked to get access to some common
actions like increase stock or change price
@@ -128,7 +128,7 @@ Alternatives
:width: 800
* detail page was intentionally left out since all information can be access
- from the update page
+ from the update page
Q&A
===
@@ -138,8 +138,4 @@ Q&A
* can we allow add extra data like order has in contractTerm?, this could be
useful for frontend apps. example of usage: country/state to where the product
- is sold since taxes may vary
-
-
-
-
+ is sold since taxes may vary
diff --git a/design-documents/018-contract-json.rst b/design-documents/018-contract-json.rst
index d7f78257..f930bcbd 100644
--- a/design-documents/018-contract-json.rst
+++ b/design-documents/018-contract-json.rst
@@ -1,5 +1,5 @@
-Design Doc 018: Forgettable Data in JSON Contract Terms
-#######################################################
+DD18: Forgettable Data in JSON Contract Terms
+#############################################
Summary
=======
@@ -92,7 +92,7 @@ scrubbed and canonicalized value is put into the special ``$forgotten$``
member of the parent object.
-.. code-block:: json
+.. code-block:: javascript
{
...props,
@@ -149,7 +149,7 @@ SHA512.
Test vector
-----------
-The follwing input contains top-level and nested forgettable
+The following input contains top-level and nested forgettable
fields, as well as booleans, integers, strings and objects
as well as non-forgettable fields. It is thus suitable as
a minimal interoperability test:
diff --git a/design-documents/019-wallet-backup-merge.rst b/design-documents/019-wallet-backup-merge.rst
index caf45401..f716c7ba 100644
--- a/design-documents/019-wallet-backup-merge.rst
+++ b/design-documents/019-wallet-backup-merge.rst
@@ -1,5 +1,5 @@
-Design Doc 019: Wallet Backup Merging
-#####################################
+DD19: Wallet Backup Merging
+###########################
Summary
=======
diff --git a/design-documents/020-backoffice-tips-management.rst b/design-documents/020-backoffice-tips-management.rst
index 566f8fb5..6049109e 100644
--- a/design-documents/020-backoffice-tips-management.rst
+++ b/design-documents/020-backoffice-tips-management.rst
@@ -1,5 +1,5 @@
-Design Doc 020: Backoffice Tips Management
-##########################################
+DD20: Backoffice Tips Management
+################################
Summary
=======
@@ -93,5 +93,3 @@ When the reserve has not yet funded
.. image:: ../backoffice-reserve-details.unfunded.svg
:width: 400
-
-
diff --git a/design-documents/021-exchange-key-continuity.rst b/design-documents/021-exchange-key-continuity.rst
index e09b6fe0..877ffdf8 100644
--- a/design-documents/021-exchange-key-continuity.rst
+++ b/design-documents/021-exchange-key-continuity.rst
@@ -1,5 +1,5 @@
-Design Doc 021: Exchange Key Continuity
-#######################################
+DD21: Exchange Key Continuity
+#############################
Summary
=======
diff --git a/design-documents/023-taler-kyc.rst b/design-documents/023-taler-kyc.rst
index 3e67bff2..bfc6e514 100644
--- a/design-documents/023-taler-kyc.rst
+++ b/design-documents/023-taler-kyc.rst
@@ -1,5 +1,5 @@
-DD 023: Taler KYC
-#################
+DD23: Taler KYC
+###############
Summary
=======
@@ -17,6 +17,8 @@ banks to identify parties involved in transactions at certain points.
Requirements
============
+The solution should support fees to be paid by the user for the KYC process (#7365).
+
Taler needs to run KYC checks in the following circumstances:
* Customer withdraws money over a monthly threshold
@@ -31,150 +33,254 @@ Taler needs to run KYC checks in the following circumstances:
* Wallet receives money via P2P payments
+ * there are two sub-cases: PUSH and PULL payments
* key: reserve (=KYC account) long term public key per wallet (encoded as payto:// URI)
* Merchant receives money (Q: any money, or above a monthly threshold?)
* key: IBAN (encoded as payto:// URI)
+* Reserve is "opened" for invoicing or tipping.
+
+ * key: reserve (=KYC account) long term public key per wallet (encoded as payto:// URI)
+
Proposed Solution
=================
-Exchange modifications
-^^^^^^^^^^^^^^^^^^^^^^
+Terminology
+^^^^^^^^^^^
-We introduce a new ``wire_targets`` table into the exchange database. This
-table is referenced as the source or destination of payments (regular deposits
-and also P2P payments). A positive side-effect is that we reduce duplication
-in the ``reserves_in``, ``wire_out`` and ``deposits`` tables as they can
-reference this table. In this table, we additionally store information
-related to the KYC status of the underlying payto://-URI.
-
-The new ``/kyc-check/`` endpoint is based on the ``wire_targets`` serial
-number. Access is ``authenticated`` by also passing the hash of the
-payto://-URI (weak authentication is acceptable, as the KYC status or the
-ability to initiate a KYC process are not very sensitive). Given this pair,
-the ``/kyc-check/`` endpoint returns either the (positive) KYC status or
-redirects the client (202) to the current stage of the KYC process. (The
-endpoint may have to create and store a nonce to be used during
-``/kyc-proof/``, depending on the OAuth variant used.) The redirection is
-offered using an HTTP-redirect for Web-based clients and a JSON body with
-information for triggering a browser-based KYC process using OAuth 2.0.
-
-The OAuth 2.0 process is setup to end at a new ``/kyc-proof/`` endpoint. This
-endpoint then updates the KYC table of the exchange with the legitimization
-status (which is checked using OAuth 2.0). The endpoint also wakes up any
-long-polling ``/kyc-check/`` requests. Naturally, the exchange's OAuth 2.0
-client credentials must be configured apriori with the legitimization service.
+* **Check**: A check establishes a particular attribute of a user, such as their name based on an ID document and lifeness, mailing address, phone number, taxpayer identity, etc.
-When withdrawing, the exchange checks if the KYC status is acceptable. If no
-KYC was done and if either the amount withdrawn over the last X days exceeds
-the threshold or the reserve received received a P2P transfer, then a ``202
-Accepted`` is returned which redirects the consumer to the new ``/kyc-check/``
-handler.
+* **Condition**: A condition specifies when KYC is required. Conditions include the *type of operation*, a threshold amount (e.g. above EUR:1000) and possibly a time period (e.g. over the last month).
-When depositing, the exchange checks the KYC status and if negative, returns an
-additional information field that tells the merchant the ``wire_target_serial``
-number needed to begin the KYC process (this is independent of the amount)
-at the new ``/kyc-check/`` handler.
+* **Configuration**: The configuration determines the *legitimization rules*, and specifies which providers offer which *checks* at what *cost*.
-When tracking deposits, the exchange also adds the ``wire_target_serial`` to
-the reply if the KYC status is negative.
+* **Cost**: Metric for the business expense for a KYC check at a certain *provider*. Not in any currency, costs are simply relative and non-negative values. Costs are considered when multiple choices are allowed by the *configuration*.
-The aggregator is modified to only SELECT deposits where the ``wire_target``
-has the KYC status set to positive (unless KYC is disabled in the exchange
-configuration).
+* **Expiration**: KYC legitimizations may be outdated. Expiration rules determine when *checks* have to be performed again.
+* **Legitimization rules**: The legitimization rules determine under which *conditions* which *checks* must be performend and the *expiration* time period for the *checks*.
- ..note::
+* **Logic**: Logic refers to a specific bit of code (realized as an exchange plugin) that enables the interaction with a specific *provider*. Logic typically requires *configuration* for access control (such as an authorization token) and possibly the endpoint of the specific *provider* implementing the respective API.
- Unrelated: We may want to consider directly deleting prewire records
- instead of setting them to ``finished`` in ``taler-exchange-transfer``.
+* **Provider**: A provider performs a specific set of *checks* at a certain *cost*. Interaction with a provider is performed by provider-specific *logic*.
+* **Type of operation**: The operation type determines which Taler-specific operation has triggered the KYC requirement. We support four types of operation: withdraw (by customer), deposit (by merchant), P2P receive (by wallet) and (high) wallet balance.
-Exchange database schema changes
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+New Endpoints
+^^^^^^^^^^^^^
-Note that there is may be some slight complication in the migration as the
-h_wire in deposits is salted, while the h_payto in the new wire_targets is
-expected to be unsalted. So converting the existing information to create the
-wire_targets table will be tricky!
+We introduce a new ``wire_targets`` table into the exchange database. This
+table is referenced as the source or destination of payments (regular deposits
+and also P2P payments). A positive side-effect is that we reduce duplication
+in the ``reserves_in``, ``wire_out`` and ``deposits`` tables as they can
+reference this table.
+
+We introduce a new ``legitimization_processes`` table that tracks the status
+of a legitimization process at a provider, including the configuration section
+name, the user/account name at the provider, and some legitimization
+identifier for the process at the provider. In this table, we additionally
+store information related to the KYC status of the underlying payto://-URI, in
+particular when the KYC expires (0 if it was never done).
+
+Finally, we introduce a new ``legitimization_requirements`` table that
+contains a list of checks required for a particular wire target. When KYC is
+triggered (say when some endpoint returns an HTTP status code of 451) a
+new requirement is first put into the requirements table. Then, when the
+client identifies as business or individual the specific legitimization
+process is started. When the taler-exchange-aggregator triggers a KYC check
+the merchant can observe this when a 202 (Accepted) status code is returned
+on GET ``/deposits/`` with the respective legitimization requirement row.
+
+The new ``/kyc-check/`` endpoint is based on the legitimization requirements
+serial number and receives the business vs. individual status from the client.
+Access is ``authenticated`` by also passing the hash of the payto://-URI.
+(Weak authentication is acceptable, as the KYC status or the ability to
+initiate a KYC process are not very sensitive.) Given this triplet, the
+``/kyc-check/`` endpoint returns either the (positive) KYC status or redirects
+the client (202) to the next required stage of the KYC process. The
+redirection must be for an HTTP(S) endpoint to be triggered via a simple HTTP GET. As this endpoint is involved in every KYC check at the beginning, this is also the place where we can
+integrate the payment process for the KYC fee.
+
+The specific KYC provider to be executed depends on the configuration (see
+below) which specifies a ``$PROVIDER_SECTION`` for each authentication procedure.
+For each (enabled) provider, the exchange has a logic plugin which
+(asynchronously) determines the redirect URL for a given wire target. See
+below for a description of the high-level process for different providers.
+
+Upon completion of the process at the KYC provider, the provider must trigger
+a GET request to a new ``/kyc-proof/$H_PAYTO/$PROVIDER_SECTION`` endpoint.
+This may be done either by redirecting the browser of the user to that
+endpoint. Once this endpoint is triggered, the exchange will pass the
+received arguments to the respective logic plugin. The logic plugin will then
+(asynchronously) update the KYC status of the user. The logic plugin should
+return a human-readable HTML page with the KYC result to the user.
+
+Alternatively, the KYC confirmation may be triggered by a ``/kyc-webhook``
+request. As KYC providers do not necessarily support passing detailed
+information in the URL arguments, the ``/kyc-webhook`` only needs to specify
+either the ``PROVIDER_SECTION`` *or* the ``LOGIC`` (the name of the plugin
+implementing the KYC API). The API-specific webhook logic must then figure
+out what exactly the webhook is about on its own. The ``/kyc-webhook/``
+endpoint works for GET or POST, again as details depend on the KYC provider.
+In contrast to ``kyc-proof``, the response does NOT go to the end-users'
+browser and should thus only indicate success or failure.
+
+
+Legitimization Hooks
+^^^^^^^^^^^^^^^^^^^^
-We can *either* not support a fully automatic migration, or do an "expensive"
-migration with C logic (so not just SQL statements).
+When withdrawing, the exchange checks if the KYC status is acceptable. If no
+KYC was done and if either the amount withdrawn over a particular timeframe
+exceeds the threshold or the reserve received received a P2P transfer, then a
+``451 Unavailable for Legal Reasons`` is returned which redirects the consumer
+to the new ``/kyc-check/`` handler.
+
+When depositing, the exchange aggregator (!) checks the KYC status and if
+negative, returns an additional information field via the
+``aggregation_transient`` table which is returned via GET ``/deposts/`` to the
+merchant. This way, the merchant learns the ``requirement_row`` needed to
+begin the KYC process (this is independent of the amount) at the new
+``/kyc-check/`` handler.
+
+When merging into a reserve, the KYC status is checked and again the
+merge fails with ``451 Unavailable for Legal Reasons`` to trigger the
+KYC process.
+
+To allow the wallet to do the KYC check if it is about to exceed a set balance
+threshold, we modify the ``/keys`` response to add an optional array
+``wallet_balance_limit_without_kyc`` of threshold amounts is returned.
+Whenever the wallet crosses one of these thresholds for the first time, it
+should trigger the KYC process. If this field is absent, there is no limit.
+If the field is provided, a correct wallet must create a long-term
+account-reserve key pair. This should be the same key that is also used to
+receive wallet-to-wallet payments. Then, *before* a wallet performs an
+operation that would cause it to exceed the balance threshold in terms of
+funds held from a particular exchange, it *should* first request the user to
+complete the KYC process.
+
+For that, the wallet should POST to the new ``/wallet-kyc`` endpoint,
+providing its long-term reserve-account public key and a signature requesting
+permission to exceed the account limit. Here, the ``balance`` specified should
+be the threshold (from the ``wallet_balance_limit_without_kyc`` array) that
+the wallet would cross, and *not* the *exact* balance of the wallet. The
+exchange will respond with a wire target UUID. The wallet can then use this
+UUID to being the KYC process at ``/kyc-check/``. The wallet must only proceed
+to obtain funds exceeding the threshold after the KYC process has
+concluded. While wallets could be "hacked" to bypass this measure (we cannot
+cryptographically enforce this), such modifications are a terms of service
+violation which may have legal consequences for the user.
+
+
+
+Configuration Options
+^^^^^^^^^^^^^^^^^^^^^
+
+The configuration specifies a set of providers, one per configuration section:
+
+[kyc-provider-$PROVIDER_ID]
+# How expensive is it to use this provider?
+# Used to pick the cheapest provider possible.
+COST = NUMBER
+# Which plugin is responsible for this provider?
+LOGIC = PLUGIN_NAME
+# Which type of user does this provider handle?
+# Either INDIVIDUAL or BUSINESS.
+USER_TYPE = INDIVIDUAL
+# Which checks does this provider provide?
+# List of strings, no specific semantics.
+PROVIDED_CHECKS = SMS GOVID PHOTO
+# Plus additional logic-specific options, e.g.:
+AUTHORIZATION_TOKEN = superdupersecret
+FORM_ID = business_legi_form
+# How long is the check considered valid?
+EXPIRATION = DURATION
+
+The configuration also specifies a set of legitimization
+requirements, one per configuration section:
+
+[kyc-legitimization-$RULE_NAME]
+# Operation that triggers this legitimization.
+# Must be one of WITHDRAW, DEPOSIT, P2P-RECEIVE
+# or WALLET-BALANCE.
+OPERATION_TYPE = WITHDRAW
+# Required checks to be performed.
+# List of strings, must individually match the
+# strings in one or more provider's PROVIDED_CHECKS.
+REQUIRED_CHECKS = SMS GOVID
+# Threshold amount above which the legitimization is
+# triggered. The total must be exceeded in the given
+# timeframe. Can be 'forever'.
+THRESHOLD = AMOUNT
+# Timeframe over which the amount to be compared to
+# the THRESHOLD is calculated.
+# Ignored for WALLET-BALANCE.
+TIMEFRAME = DURATION
+
+
+
+Exchange Database Schema
+^^^^^^^^^^^^^^^^^^^^^^^^
.. sourcecode:: sql
- -- Everything in one big transaction
- BEGIN;
- -- Check patch versioning is in place.
- SELECT _v.register_patch('exchange-TBD', NULL, NULL);
- --
CREATE TABLE IF NOT EXISTS wire_targets
(wire_target_serial_id BIGSERIAL UNIQUE
,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=64),
,payto_uri STRING NOT NULL
- ,kyc_ok BOOLEAN NOT NULL DEFAULT (false)
- ,oauth_username STRING NOT NULL
- ,PRIMARY KEY (h_wire)
- );
+ ,PRIMARY KEY (h_payto)
+ ) SHARD BY (h_payto);
COMMENT ON TABLE wire_targets
IS 'All recipients of money via the exchange';
COMMENT ON COLUMN wire_targets.payto_uri
IS 'Can be a regular bank account, or also be a URI identifying a reserve-account (for P2P payments)';
COMMENT ON COLUMN wire_targets.h_payto
IS 'Unsalted hash of payto_uri';
- COMMENT ON COLUMN wire_targets.kyc_ok
- IS 'true if the KYC check was passed successfully';
- COMMENT ON COLUMN wire_targets.oauth_username
- IS 'Name of the user that was used for OAuth 2.0-based legitimization';
- --
- -- NOTE: logic to fill wire_target missing, so this
- -- CANNOT work if the database contains any data!
- --
- ALTER TABLE wire_out
- ADD COLUMN wire_target_serial_id INT8 NOT NULL REFERENCES wire_targets (wire_target_serial_id),
- DROP COLUMN wire_target;
- COMMENT ON COLUMN wire_out.wire_target_serial_id
- IS 'Identifies the target bank account and KYC status';
- --
- ALTER TABLE reserves_in
- ADD COLUMN wire_source_serial_id INT8 NOT NULL REFERENCES wire_targets (wire_target_serial_id),
- DROP COLUMN sender_account_details;
- COMMENT ON COLUMN wire_out.wire_target_serial_id
- IS 'Identifies the target bank account and KYC status';
- --
- ALTER TABLE reserves_close
- ADD COLUMN wire_source_serial_id INT8 NOT NULL REFERENCES wire_targets (wire_target_serial_id),
- DROP COLUMN receiver_account;
- COMMENT ON COLUMN reserves_close.wire_target_serial_id
- IS 'Identifies the target bank account and KYC status. Note that closing does not depend on KYC.';
- --
- ALTER TABLE deposits
- ADD COLUMN wire_target_serial_id INT8 NOT NULL,
- ADD COLUMN salt BYTEA NOT NULL CHECK (LENGTH(salt)=64),
- DROP COLUMN h_wire,
- DROP COLUMN wire;
- COMMENT ON COLUMN deposits.wire_target_serial_id
- IS 'Identifies the target bank account and KYC status';
- -- Complete transaction
- --
- -- FIXME: 512-bit SALT is likely not specified/checked
- -- anywhere in the code (salt==string), and we probably
- -- should move to a 128-bit salt anyway!
- --
- COMMIT;
-
-
-TODO: Check if we missed miss any tables to migrate!
+
+ CREATE TABLE IF NOT EXISTS legitimization_requirements
+ (legitimization_requirement_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY
+ ,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=32)
+ ,required_checks VARCHAR NOT NULL
+ ,UNIQUE (h_payto, required_checks);
+ ) PARTITION BY HASH (h_payto);
+
+ CREATE TABLE IF NOT EXISTS legitimization_processes
+ (legitimization_serial_id BIGSERIAL UNIQUE
+ ,h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=64)
+ ,expiration_time INT8 NOT NULL DEFAULT (0)
+ ,provider_section VARCHAR NOT NULL
+ ,provider_user_id VARCHAR DEFAULT NULL
+ ,provider_legitimization_id VARCHAR DEFAULT NULL
+ ) PARTITION BY HASH (h_payto);
+
+ COMMENT ON COLUMN legitimizations.legitimization_serial_id
+ IS 'unique ID for this legitimization process at the exchange';
+ COMMENT ON COLUMN legitimizations.h_payto
+ IS 'foreign key linking the entry to the wire_targets table, NOT a primary key (multiple legitimizations are possible per wire target)';
+ COMMENT ON COLUMN legitimizations.expiration_time
+ IS 'in the future if the respective KYC check was passed successfully';
+ COMMENT ON COLUMN legitimizations.provider_section
+ IS 'Configuration file section with details about this provider';
+ COMMENT ON COLUMN legitimizations.provider_user_id
+ IS 'Identifier for the user at the provider that was used for the legitimization. NULL if provider is unaware.';
+ COMMENT ON COLUMN legitimizations.provider_legitimization_id
+ IS 'Identifier for the specific legitimization process at the provider. NULL if legitimization was not started.';
Merchant modifications
^^^^^^^^^^^^^^^^^^^^^^
+A new setting is required where the merchant backend
+can be configured for a business (default) or individual.
+
+.. note::
+
+ This still needs to be done!
+
We introduce new ``kyc_status``, ``kyc_timestamp`` and ``kyc_serial`` fields
into a new table with primary keys ``exchange_url`` and ``account``. This
status is updated whenever a deposit is created or tracked, or whenever the
@@ -206,33 +312,190 @@ long-poller return with positive news.
..note::
- Semi-related: The TMH_setup_wire_account() should be changed to use
+ Semi-related: The TMH_setup_wire_account() is changed to use
128-bit salt values (to keep ``deposits`` table small) and checks for salt
to be well-formed should be added "everywhere".
+An additional complication will arise once the exchange can trigger a KYC
+fee (402) on ``/kyc-check/``. In this case, the merchant SPA must show the QR
+code to the merchant to allow the merchant to pay the KYC fee with a wallet.
+
Bank requirements
^^^^^^^^^^^^^^^^^
-The exchange primarily requires an OAuth 2.0 login page where the user
-can either login (and share an access token that grants access to only
-the username) or register to initiate the KYC process.
+The exchange primarily requires a KYC provider to be operated by the
+bank that offers an endpoint for with an API implemented by one of
+the logic plugins (and the respective legitimization configuration).
-Alternatives
-============
+Logic plugins
+^^^^^^^^^^^^^
+
+The ``$PROVIDER_SECTION`` is based on the name of the configuration section,
+not on the name of the logic plugin (that we call ``$LOGIC``). Using the
+configuration section, the exchange then determines the logic plugin to use.
+
+This section describes the general API for all of the supported KYC providers,
+as well as some details of how this general API could be implemented by the logic for
+different APIs.
+
+
+General KYC Logic Plugin API
+----------------------------
+
+This section provides a sketch of the proposed API for the KYC logic plugins.
+
+* initiation of KYC check (``kyc-check``):
+
+ - inputs:
+ + provider_section (for additional configuration)
+ + individual or business user
+ + h_payto
+ - outputs:
+ + success/provider-failure
+ + redirect URL (or NULL)
+ + provider_user_id (or NULL)
+ + provider_legitimization_id (or NULL)
+
+* KYC status check (``kyc-proof``):
+
+ - inputs:
+ + provider_section (for additional configuration)
+ + h_payto
+ + provider_user_id (or NULL)
+ + provider_legitimization_id (or NULL)
+ - outputs:
+ + success/pending/user-aborted/user-failure/provider-failure status code
+ + HTML response for end-user
+
+* Webhook notification handler (``kyc-webhook``):
-We may not need the oauth_username, but it seems saner to store it to
-provide a link to the legitimization resource server.
+ - inputs:
+ + HTTP method (GET/POST)
+ + rest of URL (after provider_section)
+ + HTTP body (if applicable!)
+ - outputs:
+ + success/pending/user-aborted/user-failure/provider-failure status code
+ + h_payto (for DB status update)
+ + HTTP response to be returned to KYC provider
-We could also store the access token, but that seems slightly more
-dangerous and given the close business relationship is unnecessary.
+The plugins do not directly interact with the database, the caller sets the
+expiration on ``success`` and also updates ``provider_user_id`` and
+``provider_legitimization_id`` in the tables as required.
-We may want to store some additional "permission level" obtained from the
-resource server to say for which of the operations (see requirements section)
-the legitimization is sufficient.
+For the webhook, we need a way to lookup ``h_payto`` by other data, so the
+KYC logic plugin API should be provided a method lookup with:
+
+ - inputs:
+ + ``provider_section``
+ + ``provider_legitimization_id``
+ - outputs:
+ + ``h_payto``
+ + ``legitimization_process_row``
+
+
+OAuth 2.0 specifics
+-------------------
+
+In terms of configuration, the OAuth 2.0 logic requires the respective client
+credentials to be configured apriori to enable access to the legitimization
+service.
+
+For the ``/kyc-check/`` endpoint, the OAuth 2.0 logic may need to create and
+store a nonce to be used during ``/kyc-proof/``, depending on the OAuth
+variant used. This may require another exchange table. The OAuth 2.0 process
+must then be set up to end at the new ``/kyc-proof/$PROVIDER_ID/`` endpoint.
+
+This ``/kyc-proof/oauth2/`` endpoint must query the OAuth 2.0 server using the
+``code`` argument provided as a query parameter. Based on the result, it then
+updates the KYC table of the exchange with the legitimization status and
+returns a human-readable KYC status page.
+
+The ``/kyc-webhook/`` is not applicable.
+
+
+Persona specifics
+-----------------
+
+We would use the hosted flow. Endpoints return a ``request-id``, which we should
+log for diagnosis.
+
+For ``/kyc-check/``:
+
+* Post to ``/api/v1/accounts`` using ``reference-id`` set to our ``h_payto``.
+ Returns ``id`` (account_id).
+
+* Create ``/verify`` endpoint using ``template-id`` (from configuration),
+ and ``account_id`` (from previous step) and a ``reference-id`` (use
+ the ``legitimization_serial_id`` for the new process). Set
+ ``redirect-uri`` to ``/kyc-proof/$PROVIDER_ID/``. However, we cannot
+ rely on the user clicking this, so we must also configure a webhook.
+ The request returns a '``verification-id``. That we store under
+ the ``provider_legitimization_id`` in the database.
+
+For ``/kyc-proof/``:
+
+* Use the ``/api/v1/verifications`` endpoint to get the verification
+ status. Requires the ``verification-id`` from the previous step.
+ Results include: created/pending/completed/expired (aborted)/failed.
+
+For ``/kyc-webhook/``:
+
+* The webhook is authenticated using a shared secret, which should
+ be in the configuration. So all we should have to do is parse
+ the POSTed body to find the status and the ``verification-id`` to
+ lookup ``h_payto`` and return the result.
+
+
+KYC AID specifics
+-----------------
+
+For ``/kyc-check/``:
+
+* Post to ``/applicants`` with a type (person or company) to
+ obtain ``applicant_id``. Store that under ``provider_user_id``.
+ ISSUE: *we* need to get the company_name, business_activity_id
+ and registration_country before this somehow!
+
+* start with create form URL ``/forms/$FORM_ID/urls``
+ providing our ``h_payto`` as the ``external_applicant_id``,
+ using the ``applicant_id`` from above,
+ and the ``/kyc-proof/$PROVIDER_ID`` for the ``redirect_url``.
+
+* redirect customer to the ``form_url``,
+ store the ``verification_id`` under ``provider_legitimization_id``
+ in the database.
+
+For ``/kyc-proof/``:
+
+* Not needed, just return an error.
+
+For ``/kyc-webhook/``:
+
+* For security, we should probably simply trigger the GET on
+ ``/verifications/{verification_id}`` to not trust an unsigned POST
+ to tell us anything for sure. The result is then returned.
+
+
+
+Alternatives
+============
+
+We could also store the access token (returned by OAuth 2.0), but that seems
+slightly more dangerous and given the close business relationship is
+unnecessary. Furthermore, not all APIs offer this.
+
+We could extend the KYC logic API to return key attributes about the user
+(such as legal name, phone number, address, etc.) which we could then sign and
+return to the user. This would be useful in P2P payments to identify the
+origin of an invoice. However, we might want to be careful to not disclose
+the key attributes via the API by accident. This could likely be done by
+limiting access to the respective endpoint to messages with a signature by the
+reserve private key (which is the only case where we care to certify things
+anyway).
Drawbacks
diff --git a/design-documents/024-age-restriction.rst b/design-documents/024-age-restriction.rst
index c7433f00..5b6e091d 100644
--- a/design-documents/024-age-restriction.rst
+++ b/design-documents/024-age-restriction.rst
@@ -1,5 +1,5 @@
-DD 024: Anonymous Age Restriction Extension for GNU Taler
-#########################################################
+DD24: Anonymous Age Restriction Extension for GNU Taler
+#######################################################
Summary
=======
@@ -18,7 +18,7 @@ sensitive private information is disclosed.
We want to offer a better mechanism for age-restriction with GNU Taler that
* ensures anonymity and unlinkability of purchases
-* can be set to particular age groups by parents/wardens at withdrawal
+* can be set to particular age groups by parents/wardens at withdrawal
* is bound to particular coins/tokens
* can be verified by the merchant at purchase time
* persists even after refresh
@@ -29,8 +29,6 @@ optional feature that can be switched on by the exchange operator.
Requirements
============
-TODO
-
* legal requirements for merchants must allow for this kind of mechanism
@@ -55,22 +53,68 @@ protocol, that gives the minor/ward a 1/κ chance to raise the minimum age for
the new coin).
The proposed solution maintains the guarantees of GNU Taler with respect to
-anonymity and unlinkability. (TODO: refer to the paper, once published)
+anonymity and unlinkability. We have published a paper
+`Zero Knowledge Age Restriction for GNU Taler <https://link.springer.com/chapter/10.1007/978-3-031-17140-6_6>`_
+with the details.
¹) Once the feature is enabled and the age groups are defined, the exchange has
to stick to that decision until the support for age restriction is disabled.
We might reconsider this design decision at some point.
-Building Blocks
-^^^^^^^^^^^^^^^
+Main ideas and building blocks
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-TODO: Summarize the design based on the five functions ``Commit()``,
-``Attest()``, ``Verify()``, ``Derive()``, ``Compare()``.
+The main ideas are as follows:
+
+#. The exchange defines and publishes M+1 different *age groups* of increasing
+ order: :math:`0 < a_1 < \ldots < a_M` with :math:`a_i \in \mathbb{N}`. The
+ zeroth age group is :math:`\{0,\ldots,a_1-1\}`.
+
+#. An **unrestricted age commitment** is defined as a vector of length M of
+ pairs of Edx25519_ public and private keys on Curve25519. In other words: one
+ key pair for each age group after the zeroth: :math:`\bigl\langle (q_1,
+ p_1), \ldots, (q_M, p_M) \bigr\rangle`. Here, :math:`q_i` are the public keys
+ (mnemonic: **q-mitments**), :math:`p_i` are the private keys.
+
+#. A **restricted age commitment** *to age group m* is derived from an
+ unrestricted age commitment by removing all private keys for
+ indices larger than m: :math:`\bigl\langle (q_1, p_1), \ldots, (q_m, p_m),
+ \, (q_{m+1}, \perp), \ldots, (q_M, \perp )\bigr\rangle`. F.e. if *none* of
+ the private keys is provided, the age commitment would be restricted to the
+ zeroth age group.
+
+#. The act of restricting an unrestricted age commitment is performed by the
+ parent/ward.
+#. An *age commitment* (without prefix) is just the vector of public keys:
+ :math:`\vec{Q} := \langle q_1, \ldots, q_M \rangle`. Note that from
+ just the age commitment one can not deduce if it originated from an
+ unrestricted or restricted one (and what age).
+
+#. An *attestation of age group k* is essentially the signature to any message
+ with the private key for slot k, if the corresponding private key is
+ available in a restricted age commitment. (Unrestricted age commitments can
+ attest for any age group).
+
+#. An age commitment is *bound to a particular coin* by incorporating the
+ SHA256 hash value of the age commitment (i.e. the M public keys) into the
+ signature of the coin. So instead of signing :math:`\text{FDH}_N(C_p)` with
+ the RSA private key of a denomination with support for age restriction, we
+ sign :math:`\text{FDH}_N(C_p, h_Q)`. Here, :math:`C_p` is the EdDSA public
+ key of a coin and :math:`h_Q` is the hash of the age commitment :math:`\vec{Q}`.
+ **Note:** A coin with age restriction can only be validated when both, the
+ public key of the coin itself **and** the hash of the age commitment, are
+ present. This needs to be supported in each subsystem: Exchange, Wallet and
+ Merchant.
+
+
+TODO: Summarize the design based on the five functions ``Commit()``,
+``Attest()``, ``Verify()``, ``Derive()``, ``Compare()``, once the paper from
+Özgür and Christian is published.
-Changes in the Exchange
-^^^^^^^^^^^^^^^^^^^^^^^
+Changes in the Exchange API
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
The necessary changes in the exchange involve
@@ -82,49 +126,84 @@ The necessary changes in the exchange involve
Extension for age restriction
-----------------------------
+.. note::
+
+ Registering an extension is defined in
+ :doc:`design document 006 ― Extensions <006-extensions>`.
+
+
The exchange indicates support for age-restriction in response to ``/keys`` by
-registering the extension ``age_restriction.v1`` with a value type
+registering the extension ``age_restriction`` with a value type
``ExtensionAgeRestriction``:
.. ts:def:: ExtensionAgeRestriction
interface ExtensionAgeRestriction {
- // The fields ``description``and ``required`` are mandatory for each
- // extension.
-
- // Description will be something like:
- // "Support for age restriction version 1 enabled. See
- // https://docs.taler.net/design-documents/024-age-restriction.html";
- description: string;
-
- // Age restriction is not required to be supported by a wallet or
- // merchant, so ``required`` will be set to ``false``.
- required: boolean;
-
- // Age restriction specific fields.
-
- // Representation of the age groups as comma separated edges: Increasing
- // from left to right, the values mark the begining of an age group up
- // to, but not including the next value. The initial age group starts at
- // 0 and is not listed. Example: "8:10:12:14:16:18:21".
- // This field is mandatory and binding in the sense that its value is
- // taken into consideration when signing the denominations in
- // ``ExchangeKeysResponse.age_restricted_denoms``.
- age_groups: string;
+ // The field ``critical`` is mandatory for an extension.
+ // Age restriction is not required to be understood by an client, so
+ // ``critical`` will be set to ``false``.
+ critical: false;
+
+ // The field ``version`` is mandatory for an extension. It is of type
+ // `LibtoolVersion`.
+ version: "1";
+
+ // Age restriction specific configuration
+ config: ConfigAgeRestriction;
}
-Registering an extension is defined in
-:doc:`design document 006 *Extensions* <006-extensions>`.
+.. ts:def:: ConfigAgeRestriction
+
+ interface ConfigAgeRestriction {
+ // The age groups. This field is mandatory and binding in the sense
+ // that its value is taken into consideration when signing the
+ // age restricted denominations in the `ExchangeKeysResponse`
+ age_groups: AgeGroups;
+ }
+
+Age Groups
+~~~~~~~~~~
+
+Age groups are represented as a finite list of positive, increasing integers
+that mark the beginning of the *next* age group. The value 0 is omitted but
+implicitly marks the beginning of the *zeroth* age group and the first number
+in the list marks the beginning of the *first* age group. Age groups are
+encoded as a colon separated string of integer values. They are referred to by
+their *slot*, i.e. "age group 3" is the age group that starts with the 3.
+integer in the list.
+
+For example: the string "8:10:12:14:16:18:21" represents the age groups
+
+0. {0,1,2,3,4,5,6,7}
+#. {8,9}
+#. {10,11}
+#. {12,13}
+#. {14,15}
+#. {16,17}
+#. {18,19,20}
+#. {21, ⋯ }
+
+The field ``age_groups`` of type `AgeGroups` is mandatory and binding in the
+sense that its value is taken into consideration when signing the denominations
+in ``ExchangeKeysResponse.age_restricted_denoms``.
+
+.. ts:def:: AgeGroups
+
+ // Representation of the age groups as colon separated edges: Increasing
+ // from left to right, the values mark the beginning of an age group up
+ // to, but not including the next value. The initial age group starts at
+ // 0 and is not listed. Example: "8:10:12:14:16:18:21".
+ type AgeGroups = string;
+
Age restricted denominations
-----------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If age-restriction is registered as an extension under the name
-``age_restriction.v1``, as described above, the root-object
-``ExchangeKeysResponse`` in response to ``/keys`` MUST be extended by an
-additional field ``age_restricted_denoms``. This is an *additional* list of
-denominations that must be used for during modified ``refresh`` and ``deposit``
-operations (see below).
+If age-restriction is registered as extension ``age_restriction``, as described
+above, the root-object ``ExchangeKeysResponse`` in response to ``/keys`` MUST
+be extended by an additional field ``age_restricted_denoms``. This is an
+*additional* list of denominations that must be used during the modified
+``refresh`` and ``deposit`` operations (see below).
The data structure for those denominations is the same as for the regular ones
in ``ExchangeKeysResponse.denoms``. **However**, the following differences
@@ -138,6 +217,11 @@ apply for each denomination in the list:
2. The value of ``TALER_DenominationKeyValidityPS.purpose`` is set to
``TALER_SIGNATURE_MASTER_AGE_RESTRICTED_DENOMINATION_KEY_VALIDITY``.
+And similar to ``.denoms``, if the query parameter ``last_issue_date`` was
+provided by the client, the exchange will only return the keys that have
+changed since the given timestamp.
+
+
.. ts:def:: ExchangeKeysResponse
interface ExchangeKeysResponse {
@@ -145,60 +229,493 @@ apply for each denomination in the list:
// List of denominations that support age-restriction with the age groups
// given in age_groups. This is only set **iff** the extension
- // ``age_restriction.v1`` is registered under ``entensions`` with type
+ // ``age_restriction`` is registered under ``entensions`` with type
// ``ExtensionAgeRestriction``.
//
// The data structure for each denomination is the same as for the
// denominations in ExchangeKeysResponse.denoms. **However**, the
// following differences apply for each denomination in the list:
//
- // 1. The value of TALER_DenominationKeyValidityPS.denom_hash
+ // 1. The value of ``TALER_DenominationKeyValidityPS.denom_hash``
// is taken over the public key of the denomination __and__ the
// string in ``ExtensionAgeRestriction.age_groups`` from the
// corresponding extension object.
//
- // 2. The value of TALER_DenominationKeyValidityPS.purpose is set to
- // TALER_SIGNATURE_MASTER_AGE_RESTRICTED_DENOMINATION_KEY_VALIDITY
- age_restricted_denoms: Denoms[];
+ // 2. The value of ``TALER_DenominationKeyValidityPS.purpose`` is set to
+ // ``TALER_SIGNATURE_MASTER_AGE_RESTRICTED_DENOMINATION_KEY_VALIDITY``
+ //
+ // Similar as for ``.denoms``, if the query parameter ``last_issue_date``
+ // was provided by the client, the exchange will only return the keys that
+ // have changed since the given timestamp.
+ age_restricted_denoms: Denom[];
//...
}
+SQL schema
+-----------
+
+The exchange has to mark denominations with support for age restriction as such
+in the database. Also, during the melting phase of the refresh operation, the
+exchange will have to persist the SHA256 hash of the age commitment of the
+original coin.
+
+The schema for the exchange is changed as follows:
+
+.. sourcecode:: sql
+
+ -- Everything in one big transaction
+ BEGIN;
+ -- Check patch versioning is in place.
+ SELECT _v.register_patch('exchange-TBD', NULL, NULL);
+
+ -- Support for age restriction is marked per denomination.
+ ALTER TABLE denominations
+ ADD COLUMN age_restricted BOOLEAN NOT NULL DEFAULT (false);
+ COMMENT ON COLUMN denominations.age_restriced
+ IS 'true if this denomination can be used for age restriction';
+
+ -- During the melting phase of the refresh, the wallet has to present the
+ -- hash value of the age commitment (only for denominations with support
+ -- for age restriction).
+ ALTER TABLE refresh_commitments
+ ADD COLUMN age_commitment_h BYTEA CHECK (LENGTH(age_commitment_h)=64);
+ COMMENT ON COLUMN refresh_commitments.age_commitment_h
+ IS 'SHA256 hash of the age commitment of the old coin, iff the corresponding
+ denomimination has support for age restriction, NULL otherwise.';
+ COMMIT;
+
+Note the constraint on ``refresh_commitments.age_commitment_h``: It can be
+NULL, but only iff the corresponding denomination (indirectly referenced via
+table ``known_coins``) has ``.age_restricted`` set to true. This constraint
+can not be expressed reliably with SQL.
+
+
+Protocol changes
+----------------
+
+Withdraw
+~~~~~~~~
+
+The withdraw protocol is affected in the following situations:
+
+- A wire transfer to the exchange (to fill a reserve) was marked by the
+ originating bank as coming from a bank account of a minor, belonging to a of
+ a specific age group, or by other means.
+- A Peer-to-Peer transaction was performed between customers. The receiving
+ customer's KYC result tells the exchange that the customer belongs to a
+ specific age group.
+
+In these cases, the wallet will have to perform a zero-knowledge protocol with
+exchange as part of the the withdraw protocol, which we sketch here. Let
+
+- :math:`\kappa` be the same cut-and-choose parameter for the refresh-protocol.
+- :math:`\Omega \in E` be a published, nothing-up-my-sleeve, constant
+ group-element on the elliptic curve.
+- :math:`a \in \{1,\ldots,M\}` be the maximum age (group) for which the wallet
+ has to prove its commitment.
+
+The values :math:`\kappa`, :math:`\Omega` and :math:`a` are known to the
+Exchange and the Wallet. Then, Wallet and Exchange run the following protocol
+for the withdrawal of one coin:
+
+- *Wallet*
+ 1. creates planchets :math:`C_i` for :math:`i \in \{1,\ldots,\kappa\}` as candidates for *one* coin.
+ #. creates age-commitments :math:`\vec{Q}^i` for :math:`i \in \{1,\ldots,\kappa\}` as follows:
+
+ a) creates :math:`a`-many Edx25519-keypairs :math:`(p^i_j, q^i_j)`
+ randomly for :math:`j \in \{1,\ldots,a\}` (with public keys :math:`q^i_j`),
+ #) chooses randomly :math:`(M - a)`-many scalars :math:`s^i_j` for :math:`j \in \{a+1,\ldots,M\}`,
+ #) calculates :math:`\omega^i_j = s^i_j*\Omega` for :math:`j \in \{a+1,\ldots,M \}`,
+ #) sets :math:`\vec{Q}^i := (q^i_1,\ldots,q^i_a,\omega^i_{a+1},\ldots,\omega^i_M)`
+
+ #. calculates :math:`f_i := \text{FDH}(C_i, H(\vec{Q}^i))` for :math:`i \in \{ 1,\ldots,\kappa \}`.
+ #. chooses random blindings :math:`\beta_i(.)` for :math:`i \in \{1,\ldots,\kappa\}`. The blinding functions depend on the cipher (RSA, CS).
+ #. calculates :math:`F := \text{H}(\beta_1(f_1)||\ldots||\beta_\kappa(f_\kappa))`
+ #. sends :math:`F` to the Exchange
+
+- *Exchange*
+ 7. receives and stores :math:`F`
+ #. chooses randomly :math:`\gamma \in \{1,\ldots,\kappa\}` and
+ #. sends :math:`\gamma` to the Wallet.
+
+- *Wallet*
+ 10. receives :math:`\gamma`
+ #. sends to the Exchange the tuple :math:`\left(r_\gamma, \vec{\beta}, \vec{\vec{Q}}, \vec{\vec{S}}\right)` with
+
+ - :math:`r_\gamma := \beta_\gamma(f_\gamma)`
+ - :math:`\vec{\beta} := (\beta^1,\ldots,\beta^{\gamma-1},\bot,\beta^{\gamma+1},\ldots,\beta^\kappa)`
+ - :math:`\vec{\vec{Q}} := (\vec{Q}^1,\ldots,\vec{Q}^{\gamma-1},\bot,\vec{Q}^{\gamma+1},\ldots,\vec{Q}^\kappa)`
+ - :math:`\vec{\vec{S}} := (\vec{S}^1,\ldots,\vec{S}^{\gamma-1},\bot,\vec{S}^{\gamma+1},\ldots,\vec{S}^\kappa)`
+ with :math:`\vec{S}^i := (s^i_j)`
+
+- *Exchange*
+ 12. receives :math:`\left(r, (\beta^i), (\vec{Q}^i), (\vec{B}^i) \right)`
+ #. calculates :math:`g_i := \beta_i\left(\text{FDH}(\vec{Q}^i)\right)` for :math:`i \neq \gamma`
+ #. compares :math:`F \overset{?}{=} \text{H}(g_1||\ldots||g_{\gamma - 1}||r||g_{\gamma+1}||\ldots||g_\kappa)` and bails out on inequality
+ #. for each :math:`\vec{B}^i, i \neq \gamma`
+
+ i. calculates :math:`\tilde{\omega}^i_j := b^i_j * \Omega` for :math:`j \in \{a+1,\ldots,M\}`
+ #. compares each :math:`\tilde{\omega}^i_j` to :math:`q^i_j` from :math:`\vec{Q}^i = (q^i_1, \ldots, q^i_M)` and bails out on inequality
+ #. signs :math:`r`
+ #. sends (blinded) signature :math:`\sigma_r` to Wallet
+
+- *Wallet*
+ 18. receives :math:`\sigma_r`
+ #. calculates (unblinded) signature :math:`\sigma_\gamma := \beta^{-1}_\gamma(\sigma_r)` for coin :math:`C_\gamma`.
+
+
+Note that the batch version of withdraw allows the withdrawal of *multiple*
+coins at once. For that scenario the protocol sketched above is adapted to
+accomodate for handling multiple coins at once -- thus multiplying the amount
+of data by the amount of coins in question--, but all with the same value of
+:math:`\gamma`.
+
+The *actual* implementation of the protocol above will have a major optimization
+to keep the bandwidth usage to a minimum. Instead of generating and sending
+the age commitment (array of public keys) and blindings for each coin, the
+wallet *MUST* derive the corresponding blindings and the age commitments from
+the coin's private key itself as follows:
+
+Let
+
+- :math:`c_s` be the private key of the coin,
+- :math:`m \in \{1,\ldots,M\}` be the maximum age (according to the reserve)
+ that a wallet can commit to during the withdrawal.
+- :math:`P` be a published constant Edx25519-public-key to which the private
+ key is not known to any client.
+
+
+Then calculate the blinding :math:`\beta` for the coin as
+
+.. math::
+ \beta &:= \text{HKDF}(c_s, \text{"blinding"})
+
+If the denomination is using Clause-Schnorr signatures, calculate the nonce
+:math:`n` for the coin as
+
+.. math::
+ n &:= \text{HKDF}(c_s, \text{"cs-nonce"})
+
+
+
+For the age commitment, calculate:
+
+1. For age group :math:`a \in \{1,\ldots,m\}`, set
+
+.. math::
+ s_a &:= \text{HDKF}(c_s, \text{"age-commitment"}, a) \\
+ p_a &:= \text{Edx25519\_generate\_private}(s_a) \\
+ q_a &:= \text{Edx25519\_public\_from\_private}(p_a)
+
+2. For age group :math:`a \in \{m,\ldots,M\}`, set
+
+.. math::
+ f_a &:= \text{HDKF}(c_s, \text{"age-factor"}, a) \\
+ q_a &:= \text{Edx25519\_derive\_public}(P, f_a).
+
+Then the vector :math:`\vec{q} = \{q_1,\ldots,q_M\}` is then the age commitment
+associated to the coin's private key :math:`c_s`. For the non-disclosed coins,
+the wallet can use the vector :math:`(p_1,\ldots,p_m,\bot,\ldots,\bot)` of
+private keys for the attestation.
+
+Provided with the private key :math:`c_s`, the exchange can therefore calculate
+the blinding :math:`\beta`, the nonce :math:`n` (if needed) and the age
+commitment :math:`\vec{q}` itself, along with the coin's public key :math:`C_p`
+and use the value of
+
+.. math::
+
+ \text{TALER\_CoinPubHashP}(C_p, \text{age\_commitment\_hash}(\vec{q}))
+
+during the verification of the original age-withdraw-commitment.
+
+
+For the withdrawal with age restriction, a sketch of the corresponding database
+schema in the exchange is given here:
+
+.. graphviz::
+
+ digraph deposit_policies {
+ rankdir = LR;
+ splines = true;
+ fontname="monospace"
+ node [
+ fontname="monospace"
+ shape=record
+ ]
+
+ subgraph cluster_commitments {
+ label=<<B>withdraw_age_commitments</B>>
+ margin=20
+ commitments [
+ label="<id>withdraw_age_commitments_id\l|h_commitment\l|amount_with_fee_val\l|amount_with_fee_frac\l|noreveal_index\l|max_age_group\l|<res>reserve_pub\l|reserve_sig\l|timestamp\l"
+ ]
+ }
+
+ subgraph cluster_reveals {
+ label=<<B>withdraw_age_reveals</B>>
+ margin=20
+ reveals [
+ label="freshcoin_index\l|<comm>withdraw_age_commitments_id\l|<denom>denominations_serial\l|h_coin_ev\l"
+ ]
+ }
+
+ commitments:res->reserves:id [ label="n:1"; fontname="monospace"];
+ reveals:comm -> commitments:id [ label="n:1"; fontname="monospace" ];
+ reveals:denom -> denominations:id [ label="n:1"; fontname="monospace"] ;
+
+ }
+
+
+Refresh - melting phase
+~~~~~~~~~~~~~~~~~~~~~~~
+
+During the melting phase of the refresh, the wallet has to present the hash
+value of the age commitment (for denominations with support for age
+restriction). Therefore, in the ``/coins/$COIN_PUB/melt`` POST request, the
+``MeltRequest`` object is extended with an optional field
+``age_commitment_hash``:
+
+.. ts:def:: MeltRequest
+
+ interface MeltRequest {
+ ...
+
+ // SHA256 hash of the age commitment of the coin, IFF the denomination
+ // has age restriction support. MUST be omitted otherwise.
+ age_commitment_hash?: AgeCommitmentHash;
+
+ ...
+ }
+
+.. ts:def:: AgeCommitmentHash
+
+ type AgeCommitmentHash = SHA256HashCode;
+
+The responses to the POST request remain the same.
+
+For normal denominations *without* support for age restriction, the calculation
+for the signature check is as before (borrowing notation from
+`Florian's thesis <https://taler.net/papers/thesis-dold-phd-2019.pdf>`_):
+
+.. math::
+ \text{FDH}_N(C_p)\; \stackrel{?}{=}\; \left(\sigma_C\right)^{e} \;\;\text{mod}\,N
+
+Here, :math:`C_p` is the EdDSA public key of a coin, :math:`\sigma_C` is its
+signature and :math:`\langle e, N \rangle` is the RSA public key of the
+denomination.
+
+For denominations *with* support for age restriction, the exchange takes the
+hash value ``age_commitment_hash`` (abbreviated as :math:`h_a`) into account
+when verifying the coin's signature:
+
+.. math::
+ \text{FDH}_N(C_p, h_a)\; \stackrel{?}{=}\; \left(\sigma_C\right)^{e} \;\;\text{mod}N
+
+
+
+
+Refresh - reveal phase
+~~~~~~~~~~~~~~~~~~~~~~
+
+During the reveal phase -- that is upon POST to ``/refreshes/$RCH/reveal`` --
+the client has to provide the original age commitment of the old coin (i.e. the
+vector of public keys), iff the corresponding denomination had support for age
+restriction. The size of the vector is defined by the Exchange implicetly as
+the amount of age groups defined in the field ``.age_groups`` of the
+``ExtensionAgeRestriction``.
+
+.. ts:def:: RevealRequest
+
+ interface RevealRequest {
+ ...
+
+ // Iff the corresponding denomination has support for age restriction,
+ // the client MUST provide the original age commitment, i.e. the vector
+ // of public keys.
+ // The size of the vector is defined by the Exchange implicetly as the
+ // amount of age groups defined in the field ``.age_groups`` of the
+ // ``ExtensionAgeRestriction``.
+ old_age_commitment?: Edx25519PublicKey[];
+
+
+ ...
+ }
+
+
+The exchange can now check if the provided public keys ``.old_age_commitment``
+have the same SHA256 hash value when hashed in sequence as the
+``age_commitment_hash`` of the original coin from the call to melt.
+
+The existing `cut&choose protocol during the reveal phase
+</core/api-exchange.html#post--refreshes-$RCH-reveal>`__ is extended to perform
+the following additional computation and checks:
-Refresh
--------
+Using the κ-1 transfer secrets :math:`\tau_i` from the reveal request, the
+exchange derives κ-1 age commitments from the ``old_age_commitment`` by calling
+``Edx25519_derive_public()`` on each `Edx25519PublicKey`, with :math:`\tau_i`
+as the seed, and then calculates the corresponding κ-1 hash values :math:`h_i`
+of those age commitments.
-TODO: Extension of the cut'n-choose-protocol.
+It then calculates the κ-1 blinded hashes
+:math:`m_i = r^{e_i}\text{FDH}_N(C^{(i)}_p, h_i)` (using the notation from Florian's
+thesis) of the disclosed coins and together with the :math:`m_\gamma` of the
+undisclosed coin, calculates the hash
+:math:`h'_m = H(m_1,\cdots,m_\gamma,\cdots,m_\kappa)` which is then used in the
+final verification step of the cut&choose protocol.
Deposit
--------
+~~~~~~~
-TODO: Add opaque hash value of the commitments to the protocol
+As always, the merchant has to provide the public key of a coin during a POST
+to ``/coins/$COIN_PUB/deposit``. However, for coins with age restriction, the
+signature check requires the hash of the age commitment. Therefore the request
+object ``DepositRequest`` is extended by an optional field
+``age_commitment_hash`` which MUST be set (with the SHA256 hash of the age
+commitment), iff the corresponding denomination had support for age restriction
+enabled. The merchant has received this value prior from the customer during
+purchase.
-Changes in the Merchant
-^^^^^^^^^^^^^^^^^^^^^^^
+.. ts:def:: DepositRequest
-TODO: Spending protocol
+ interface DepositRequest {
+ ...
+ // Iff the corresponding denomination had support for age restriction
+ // enabled, this field MUST contain the SHA256 value of the age commitment that
+ // was provided during the purchase.
+ age_commitment_hash?: AgeCommitmentHash;
-Changes in the Wallet
-^^^^^^^^^^^^^^^^^^^^^
+ ...
+ }
+
+Again, the exchange can now check the validity of the coin with age restriction
+by evaluating
+
+.. math::
+ \text{FDH}_N(C_p, h_a)\; \stackrel{?}{=}\; \left(\sigma_C\right)^{e} \;\;\text{mod}N
+
+Also again, :math:`C_p` is the EdDSA public key of a coin, :math:`\sigma_C` is
+its signature, :math:`\langle e, N \rangle` is the RSA public key of the
+denomination and :math:`h_a` is the value from ``age_commitment_hash``.
+
+
+
+Changes in the Merchant API
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+
+Claiming the order
+------------------
+
+If an order requires a minimum age, the merchant MUST express that required
+minimum age in response to order claim by the wallet, that is, a POST to
+``[/instances/$INSTANCE]/orders/$ORDER_ID/claim``.
+
+The object ``ContractTerms`` is extended by an optional field
+``minimum_age`` that can be any integer greater than 0. In reality
+this value will not be smaller than, say, 8, and not larger than, say, 21.
+
+.. ts:def:: ContractTerms
+
+ interface ContractTerms {
+ ...
+
+ // If the order requires a minimum age greater than 0, this field is set
+ // to the integer value of that age. In reality this value will not be
+ // smaller than, say, 8, and not larger than, say, 21.
+ minimum_age?: Integer;
+
+ ...
+ }
+
+By sending the contract term with the field ``minimum_age`` set to an
+non-zero integer value, the merchant implicetly signals that it understands the
+extension ``age_restriction`` for age restriction from the exchange.
+
+
+Making the payment
+------------------
+
+If the ``ContractTerms`` had a non-zero value in field
+``minimum_age``, the wallet has to provide evidence of that minimum
+age by
+
+#. *either* using coins which are of denominations that had *no* age support
+ enabled,
+
+#. *or* using coins which are of denominations that have support for age
+ restriction enabled
-TODO.
+ * and then ―for each such coin― it has the right private key of the
+ restricted age commitment to the age group into which the required minimum
+ age falls (i.e. a non-empty entry at the right index in vector of Edx25519
+ keys, see above).
-* choosing age-restriction during withdrawal coins from denominations with
- support for age restriction.
-* Define protocol to pass denominations to child/ward.
+ * and signs the required minimum age with each coin's private key
+ corresponding to the age group,
+ * and sends ―for each coin― the complete age commitment and the signature to
+ the merchant.
+
+The object ``CoinPaySig`` used within a ``PayRequest`` during a POST to
+``[/instances/$INSTANCE]/orders/$ORDER_ID/pay`` is extended as follows:
+
+.. ts:def:: CoinPaySig
+
+ export interface CoinPaySig {
+ ...
+
+ // If a minimum age was required by the order and the wallet had coins that
+ // are at least committed to the corresponding age group, this is the
+ // signature of the minimum age as a string, using the private key to the
+ // corresponding age group.
+ minimum_age_sig?: Edx25519Signature;
+
+ // If a minimum age was required by the order, this is age commitment bound
+ // to the coin, i.e. the complete vector of Edx25519_ public keys, one for each
+ // age group (as defined by the exchange).
+ age_commitment?: Edx25519PublicKey[];
+
+ }
+
+
+The merchant can now verify
+
+#. the validity of each (age restricted) coin by evaluating
+
+ .. math:: \text{FDH}_N(C_p, h_a)\; \stackrel{?}{=}\; \left(\sigma_C\right)^{e} \;\;\text{mod}N
+
+ Again, :math:`C_p` is the EdDSA public key of a coin, :math:`\sigma_C` is
+ its signature, :math:`\langle e, N \rangle` is the RSA public key of the
+ denomination and :math:`h_a` is the SHA256 hash value of the vector in
+ ``age_commitment``.
+
+#. the minimum age requirement by checking the signature in ``minimum_age_sig``
+ against the public key ``age_commitment[k]`` of the corresponding age group,
+ say, ``k``. (The minimum age must fall into the age group at index ``k`` as
+ defined by the exchange).
+
+**Note**: This applies only to coins for denominations that have support for
+age restriction. Denominations *without* support for age restriction *always*
+satisfy any minimum age requirement.
+
+
+
+Changes in the Wallet
+^^^^^^^^^^^^^^^^^^^^^
+
+A wallet implementation SHOULD support denominations with age restriction. In
+that case it SHOULD allow to select an age group as upper bound during
+withdraw.
Alternatives
============
-TODO.
-
* ID-based systems
* credit/debit card based systems
@@ -206,12 +723,10 @@ TODO.
Drawbacks
=========
-TODO.
-
* age groups, once defined, are set permanently
-* age restricted coins are basically shared between ward and warden.
Also discuss:
+
* storage overhead
* computational overhead
* bandwidth overhead
@@ -220,19 +735,115 @@ Also discuss:
Discussion / Q&A
================
-We had some very engaged discussions on the GNU Taler mailing list
-`<mailto:taler@gnu.org>`:
+We had some very engaged discussions on the GNU Taler `mailing list <taler@gnu.org>`__:
+
+* `Money with capabilities <https://lists.gnu.org/archive/html/taler/2021-08/msg00005.html>`_
+
+* `On age-restriction (was: online games in China) <https://lists.gnu.org/archive/html/taler/2021-09/msg00006.html>`__
+
+* `Age-restriction is about coins, not currencies <https://lists.gnu.org/archive/html/taler/2021-09/msg00021.html>`__
+
+* The published paper: `Zero Knowledge Age Restriction for GNU Taler <https://link.springer.com/chapter/10.1007/978-3-031-17140-6_6>`_
+
+
+.. _Edx25519:
+
+Edx25519
+========
+
+Edx25519 is a variant of EdDSA on curve25519 which allows for repeated
+derivation of private and public keys, independently. It is implemented in
+`GNUNET with commit ce38d1f6c9bd7857a1c3bc2094a0ee9752b86c32.
+<https://git.gnunet.org/gnunet.git/commit/?id=ce38d1f6c9bd7857a1c3bc2094a0ee9752b86c32>`__
+
+The private keys in Edx25519 initially correspond to the data after expansion
+and clamping in EdDSA. However, this correspondence is lost after deriving
+further keys from existing ones. The public keys and signature verification
+are compatible with EdDSA.
+
+The scheme is as follows:
+
+::
+
+ /* Private keys in Edx25519 are pairs (a, b) of 32 byte each.
+ * Initially they correspond to the result of the expansion
+ * and clamping in EdDSA.
+ */
+
+ Edx25519_generate_private(seed) {
+ /* EdDSA expand and clamp */
+ dh := SHA-512(seed)
+ a := dh[0..31]
+ b := dh[32..64]
+ a[0] &= 0b11111000
+ a[31] &= 0b01111111
+ a[31] |= 0b01000000
+
+ return (a, b)
+ }
+
+ Edx25519_public_from_private(private) {
+ /* Public keys are the same as in EdDSA */
+ (a, _) := private
+ return [a] * G
+ }
+
+ Edx25519_blinding_factor(P, seed) {
+ /* This is a helper function used in the derivation of
+ * private/public keys from existing ones. */
+ h1 := HKDF_32(P, seed)
+
+ /* Ensure that h == h % L */
+ h := h1 % L
+
+ /* Optionally: Make sure that we don't create weak keys. */
+ P' := [h] * P
+ if !( (h!=1) && (h!=0) && (P'!=E) ) {
+ return Edx25519_blinding_factor(P, seed+1)
+ }
+
+ return h
+ }
+
+ Edx25519_derive_private(private, seed) {
+ /* This is based on the definition in
+ * GNUNET_CRYPTO_eddsa_private_key_derive. But it accepts
+ * and returns a private pair (a, b) and allows for iteration.
+ */
+ (a, b) := private
+ P := Edx25519_public_key_from_private(private)
+ h := Edx25519_blinding_factor(P, seed)
-* Money with capabilities
- `<https://lists.gnu.org/archive/html/taler/2021-08/msg00005.html>`_
-
+ /* Carefully calculate the new value for a */
+ a1 := a / 8;
+ a2 := (h * a1) % L
+ a' := (a2 * 8) % L
-* On age-restriction (was: online games in China)
- `<https://lists.gnu.org/archive/html/taler/2021-09/msg00006.html>`_
+ /* Update b as well, binding it to h.
+ This is an additional step compared to GNS. */
+ b' := SHA256(b ∥ h)
-* Age-restriction is about coins, not currencies
- `<https://lists.gnu.org/archive/html/taler/2021-09/msg00021.html>`_
+ return (a', b')
+ }
+ Edx25519_derive_public(P, seed) {
+ h := Edx25519_blinding_factor(P, seed)
+ return [h]*P
+ }
-The upcoming paper on anonymous age-restriction for GNU Taler from Özgür Kesim
-and Christian Grothoff will be cited here, once it is published.
+ Edx25519_sign(private, message) {
+ /* As in Ed25519, except for the origin of b */
+ (d, b) := private
+ P := Edx25519_public_from_private(private)
+ r := SHA-512(b ∥ message)
+ R := [r] * G
+ s := r + SHA-512(R ∥ P ∥ message) * d % L
+
+ return (R,s)
+ }
+
+ Edx25519_verify(P, message, signature) {
+ /* Identical to Ed25519 */
+ (R, s) := signature
+ return [s] * G == R + [SHA-512(R ∥ P ∥ message)] * P
+ }
diff --git a/design-documents/025-withdraw-from-wallet.rst b/design-documents/025-withdraw-from-wallet.rst
new file mode 100644
index 00000000..a00d7a1b
--- /dev/null
+++ b/design-documents/025-withdraw-from-wallet.rst
@@ -0,0 +1,66 @@
+DD25: Withdraw coins manually starting from the wallet
+######################################################
+
+Summary
+=======
+
+This document presents the design discussion about the manual withdraw screens
+
+Motivation
+==========
+
+To have a way to initiate a withdrawal process and complete with a bank that is
+not aware of GNU Taler
+
+Proposed Solution
+=================
+
+Access to the feature
+^^^^^^^^^^^^^^^^^^^^^
+
+Adding a withdraw button in the main balance page to initiate the process.
+
+This feature can be use in the Pay call-to-action when there is not enough coins.
+
+Start
+^^^^^
+
+This screen the user will be able to select the currency from a list of known
+currencies, switch the exchange, go to a page to add an exchange and define an
+amount to be withdraw.
+
+.. image:: ../wallet-start-manual-withdraw.svg
+ :width: 800
+
+
+Success
+^^^^^^^
+
+Here the user will see the account details where it needs to send money to
+complete the withdrawal.
+
+.. image:: ../wallet-confirm-withdraw.svg
+ :width: 800
+
+Transaction history
+^^^^^^^^^^^^^^^^^^^
+
+The account information should be added into the Withdrawal Transaction detail
+screen if the withdrawal is still pending. This will also affect the use case
+when the user started the transaction from a taler-aware bank (in which case the
+user doesn't need to do an extra step to complete the process) so the text
+should be consistent in both scenarios.
+
+Alternatives
+============
+
+* removing the amount field, the exchange will send coins equal to the amount it received
+
+* showing the fees, can we calculate the withdrawal fee?
+
+* should we show the terms of service?
+
+* exchange field first has been discussed, but the exchange list its only showing the
+ current currency exchanges, the user need to switch the currency first. Adding a
+ new exchange should be done in a different context that can be accessed using the
+ ``add exchange`` link
diff --git a/design-documents/026-refund-fees.rst b/design-documents/026-refund-fees.rst
new file mode 100644
index 00000000..b6a31fe0
--- /dev/null
+++ b/design-documents/026-refund-fees.rst
@@ -0,0 +1,61 @@
+DD26: Refunds and Fees
+######################
+
+Summary
+=======
+
+This document discusses what should happen with deposit fees when a
+deposit is refunded.
+
+
+Motivation
+==========
+
+When a user receives a refund, we have to decide what happens to the deposit
+fee that was originally paid on the associated deposit. Originally, we said
+that the deposit fee is waived when any refund happens. However, if the
+refund fee is zero and the deposit fee is non-zero, this results in a
+problematic scenario where merchants issue minuscule refunds that primarily
+enable customers to effectively obtain the deposit fee.
+
+
+Requirements
+============
+
+ * If the refund and refresh fees are zero, it should be possible for
+ consumers to get 100% of their digital cash back on refunds.
+ * This should not result in a problematic situation where merchants
+ conspire with consumers and issue minuscule refunds to allow consumers
+ to work around deposit fees.
+
+Proposed Solution
+=================
+
+ * Only waive the deposit fee for full refunds where for the
+ specific coin (!) the refunded amount is the total value of the
+ refunded deposit.
+
+Alternatives
+============
+
+ * Only waive the deposit fee for full refunds where for the
+ specific coin the refunded amount is the total value of the
+ denomination of the coin. This may slightly simplify the
+ logic, but has the problem that it does not enable 100%
+ refunds if the original payment already required a refresh
+ because the coin's value exceeded the paid amount.
+ * Waive the deposit fee on any (including partial) refund.
+ This creates a bad incentive structure if combined refresh
+ and refund fees are below deposit fees.
+
+Drawbacks
+=========
+
+ * We need to update and test an already complex fee calculation
+ logic.
+
+
+Discussion / Q&A
+================
+
+(This should be filled in with results from discussions on mailing lists / personal communication.)
diff --git a/design-documents/027-sandboxing-taler.rst b/design-documents/027-sandboxing-taler.rst
new file mode 100644
index 00000000..c671e55d
--- /dev/null
+++ b/design-documents/027-sandboxing-taler.rst
@@ -0,0 +1,165 @@
+DD27: Sandboxing all the Taler services
+#######################################
+
+.. note::
+
+ This design document is currently a draft, it
+ does not reflect any implementation decisions yet.
+
+Summary
+=======
+
+This document presents a method of deploying all the Taler
+services via one Docker container.
+
+Motivation
+==========
+
+It is very difficult to build GNU Taler from scratch. It is even more difficult
+to install, configure and launch it correctly.
+
+The purpose of the sandbox is to have a demonstration system that can be both
+build and launched with ideally a single command.
+
+Requirements
+============
+
+- No external services should be required, the only dependencies should be:
+
+ - podman/docker
+ - optionally: configuration files to further customize the setup
+
+- All services that are used should be installed from repositories
+ and not built from scratch (i.e. debian repos or PyPI)
+
+- There should be some "admin page" for the whole sandbox that:
+
+ - Show an overview of all deployed services, a link to their documentation
+ and the endpoints they expose
+ - Show very simple statistics (e.g. number of transactions / withdrawals)
+ - Allows generating and downloading the auditor report
+
+- Developers should be able to launch the sandbox on their own machine
+
+ - Possibly using nightly repos instead of the official stable repos
+
+- We should be able to deploy it on $NAME.sandbox.taler.net
+
+Design
+======
+
+The container is based on Debian Sid, and it installs all
+the services from their Debian packages. During the build
+process, it creates all the 'static' configuration. This
+one includes all the .conf-files, the database setup and the
+keying.
+
+Subsequently at the launch step, the system will create all
+the remaining RESTful resources. Such RESTful resources include
+the merchant instances and all the euFin accounts, both at Sandbox
+and at Nexus.
+
+The sandbox will serve one HTTP base URL and make any service
+reachable at $baseUrl/$service. For example, the exchange base
+URL will be "$baseUrl/exchange".
+
+The sandbox allows to configure:
+
+- which host it binds to, typically localhost+port.
+- which host is being reverse proxied to the sandbox. This
+ helps to generate valid URIs of services.
+
+All the other values will be hard-coded in the preparation.
+
+The database is aunched *in* the same container along the
+other services.
+
+Open questions
+==============
+
+- How to collect the static configuration values?
+
+ - => Via a configuration file that you pass to the container via
+ a mounted directory (=> ``-v $MYCONFIG:/sandboxconfig``)
+ - If we don't pass any config, the container should have
+ sane defaults
+ - This is effectively a "meta configuration", because it will
+ be used to generate the actual configuration files
+ and do RESTful configuration at launch time.
+
+- How to persist, at build time, the information
+ needed later at launch time to create the RESTful
+ resources?
+
+ - => The configuration should be done at launch-time of the container.
+
+- Should we at this iteration hard-code passwords too?
+ With generated passwords, (1) it won't be possible to
+ manually log-in to services, (2) it won't be possible
+ to write the exchange password for Nexus in the conf.
+ Clearly, that's a problem when the sandbox is served
+ to the outside.
+
+- How is data persisted? (i.e. where do we store stuff)
+
+ - By allowing to mount some data directory on the host of the container
+ (This stores the DB files, config files, key files, etc.)
+ - ... even for data like the postgresql database
+ - future/optional: we *might* allow connection to an external postgresql database as well
+
+- How are services supervised?
+
+ - SystemD? gnunet-arm? supervisord? something else?
+
+ - SystemD does not work well inside containers
+
+ - alternative: one container per service, use (docker/podman)-compose
+
+ - Either one docker file per service, *or* one base container that
+ can be launched as different services via command line arg
+
+ - Advantage: It's easy to see the whole architecture from the compose yaml file
+ - Advantage: It would be easy to later deploy this on kubernetes etc.
+
+ - list of containers:
+
+ - DB container (postgres)
+ - Exchange container (contains all exchange services, for now)
+ - Split this up further?
+ - Merchant container
+
+- Do we have multi-tenancy for the sandbox? (I.e. do we allow multiple
+ currencies/exchanges/merchants/auditors per sandbox)
+
+ - Might be simpler if we disallow this
+
+- How do we handle TLS
+
+ - Do we always do HTTPs in the sandbox container?
+ - We need to think about external and internal requests
+ to the sandbox
+
+- How do we handle (external vs internal) URLs
+
+ - If we use http://localhost:$PORT for everything, we can't expose
+ the services externally
+ - Example 1: Sandbox should run on sb1.sandbox.taler.net.
+
+ - What will be the base URL for the exchange in the merchant config?
+ - If it's https://sb1.sandbox.taler.net/exchange, we need some /etc/hosts entry
+ inside the container
+ - Once you want to expose the sandbox internally, you need a proper TLS cert (i.e. letsencrypt)
+ - Inside the container, you can get away with self-signed certificates
+ - Other solution: Just require the external nginx (e.g. at gv) to reverse proxy
+ sb1.sandbox.taler.net back to the container. This means that all communication
+ between services inside the sandbox container goes through gv
+
+ - Not great, but probably fine for first iteration
+ - Disadvantages: To test the container in the non-localhost mode, you need the external proxy running
+
+- Where do we take packages from?
+
+ - By default, from the stable taler-systems.com repos and PyPI
+ - Alternatively, via the nightly gv debian repo
+ - Since we install packages at container build time, this setting (stable vs nightly)
+ results in different container base images
diff --git a/design-documents/028-deposit-policies.rst b/design-documents/028-deposit-policies.rst
new file mode 100644
index 00000000..d0a1b107
--- /dev/null
+++ b/design-documents/028-deposit-policies.rst
@@ -0,0 +1,188 @@
+DD28: Deposit Policy Extensions
+###############################
+
+.. note::
+
+ This is Work-In-Progress.
+
+Summary
+*******
+
+We will propose here a plugable mechanism in the exchange to support deposits
+with associated policy. An exchange can enable support for such policies via
+configuration.
+
+The inital set of policy extensions that an exchange might provide consists of
+
+Merchant refunds
+ Merchant can grant customers refundable payments. In this case, the
+ amount of the deposit is put into escrow by the exchange for a certain
+ period until which the customer can claim a refund.
+
+Escrowed payments
+ A trustor puts coins into escrow with the exchange. It can be claimed
+ by a beneficiary until a certain deadline, when the claim is signed by
+ both, the beneficiary's and the trustor's keys.
+
+Brandt-Vickrey auctions
+ A bidder puts coins into escrow with the exhange in order to
+ participate in an Brandt-Vickrey auction. The deposit confirmation is
+ proof to the seller for the escrow and contains a hash of the auction
+ meta-data and a deadline. After successfull execution of the auction,
+ the seller provides a valid transcript to the exchange from which the
+ exchange learns which bidder(s) won the auction for which prices. It
+ then transfers the amounts from the winners' coins to the seller. In
+ case of a timeout and for all losing bidders, the coins can be
+ refreshed.
+
+The policies shall be implemented as *extensions* to the exchange (see
+:doc:`006-extensions`).
+
+Motivation
+**********
+
+TODO
+
+Background and Requirements
+***************************
+
+TODO
+
+Proposed Solution
+*****************
+
+TODO, explain:
+
+- C-structs for policy extensions (esp. the handlers)
+- Naming conventions for policy extensions
+- Deadlines and -handling
+- API-endpoints (``/extensions/policy_...``)
+- Typical choreography of a deposit with policy and its fulfillment
+
+Database-schema
+===============
+
+TODO: Description
+
+.. graphviz::
+
+ digraph deposit_policies {
+ rankdir = LR;
+ splines = false;
+ fontname="monospace"
+ node [
+ fontname="monospace"
+ shape=record
+ ]
+
+ subgraph cluster_deposits {
+ label=<<B>deposits</B>>
+ margin=20
+ deposits [
+ label="...|<ref>policy_details_id (null)\l|...|timestamp\l|..."
+ ]
+ }
+
+ subgraph cluster_policy_details {
+ label=<<B>policy_details</B>>
+ margin=20
+ policy_details [
+ label="<id>id\l|<hash>policy_hash_code (unique)\l|deadline\l|commitment (amount)\l|accumulated_total (amount)\l|fee (amount)\l|transferable (amount)\l|fulfillment_state\l|<fid>fulfillment_id (null)\l"
+ ]
+ }
+
+ subgraph cluster_policy_fulfillments {
+ label=<<B>policy_fulfillments</B>>
+ margin=20
+ rank=min;
+ policy_fulfillments [
+ label="<id>id\l|proof\l|timestamp\l|<codes>policy_hash_codes (blob)\l"
+ ]
+ }
+
+ deposits:ref->policy_details:id [ label="n:1"; fontname="monospace" ];
+ policy_details:fid->policy_fulfillments:id [label="n:1"; fontname="monospace" ];
+ }
+
+
+The field ``policy_hash_codes`` in table ``policy_fulfillments`` is a binary
+blob that consists of the concatenation of the sorted
+``policy_details.policy_hash_code`` entries from all policies that are fulfilled by
+this proof.
+
+
+Policy Fulfillment States
+=========================
+
+The fulfillment of a policy can be in one of the following five states:
+
+Ready
+ The policy is funded and ready. The exchange is waiting for a proof of
+ fulfillment to arrive before the deadline.
+
+Insufficient
+ The policy lacks funding, that is ``accumulated_total`` <
+ ``commitment``, but has otherwise been accepted. Funding can be
+ continued by calling ``/deposit`` or ``/batch-deposit`` with more coins
+ and the same policy details.
+
+Success
+ The policy is provably fulfilled. The amounts for payout, fees and
+ refresh are transfered/can be claimed. Note that a policy fulfillment
+ handler can change the values for the amounts for payout, fees and
+ refresh.
+
+Timeout
+ The policy has timed out. The amounts for payout and refresh are
+ transfered/can be claimed.
+
+Failure
+ The policy is in an failure state. Payouts and refreshes are
+ blocked, timeouts are ignored.
+
+
+
+Invariants
+^^^^^^^^^^
+
+The following invariants need to be fulfilled and be checked by the auditor:
+
+- The fulfillment state of a policy is **Insufficient** IF AND ONLY IF the
+ amount in ``policy_details.commitment`` is equal or larger than the amount in
+ ``policy_details.accumulated_total``.
+
+- The sum of amounts in ``policy_details.fee`` and
+ ``policy_details.transferable`` MUST be equal or less than the amount in
+ ``policy_details.accumulated_total``.
+
+- The amount in ``policy_details.accumulated_total`` MUST be equal to the total
+ sum of contributions of the individual coins of the deposits that reference
+ this policy.
+
+- Each hash code encoded in ``policy_fulfillments.policy_hash_codes`` MUST
+ refer to an existing ``policy_details.hash_code`` AND its ``.fulfillment_id``
+ MUST point to the same ``policy_fulfillments.id``.
+
+- Conversely: If a ``policy_details.fulfillment_id`` points to an entry in
+ ``policy_fulfillment``, the ``policy_details.policy_hash_code`` MUST be
+ present in that entry's ``.policy_hash_codes``.
+
+
+
+Alternatives
+============
+
+TODO
+
+Drawbacks
+=========
+
+TODO
+
+
+Discussion / Q&A
+================
+
+TODO
+
+(This should be filled in with results from discussions on mailing lists / personal communication.)
diff --git a/design-documents/029-mobile-ui.rst b/design-documents/029-mobile-ui.rst
new file mode 100644
index 00000000..ddb5b0ca
--- /dev/null
+++ b/design-documents/029-mobile-ui.rst
@@ -0,0 +1,41 @@
+DD29: Mobile P2P UI
+###################
+
+Summary
+=======
+
+Design the ui and interaction of p2p payments using mobile
+
+Motivation
+==========
+
+The wallet user should be able to easily send and receive money to and from other wallet.
+
+Requirements
+============
+
+ * It should work even if the user does not have other communication channel
+ * Use the swipe up (to send) and swipe down (to request) gesture as other apps do
+ * It should be able to request payment before any withdraw (currency still unknown)
+ * It should support multi-device
+ * The user may opt-in to be findable through a registry service
+
+Proposed Solution
+=================
+
+.. image:: ../wallet-mobile-overview.svg
+ :width: 800
+
+
+Alternatives
+============
+
+Drawbacks
+=========
+
+
+
+Discussion / Q&A
+================
+
+(This should be filled in with results from discussions on mailing lists / personal communication.)
diff --git a/design-documents/030-offline-payments.rst b/design-documents/030-offline-payments.rst
new file mode 100644
index 00000000..f2b3027e
--- /dev/null
+++ b/design-documents/030-offline-payments.rst
@@ -0,0 +1,99 @@
+DD30: Offline payments
+######################
+
+Summary
+=======
+
+This design document discusses support for offline payments.
+
+Motivation
+==========
+
+Many proposed CBDCs claim to support offline payments.
+Taler is explicitly meant to be an online payment system. However, since there
+recently seems to be an increased interest in offline CBDC solutions,
+we have decided to still explore how Taler could support offline payments in the future.
+
+While we still recommend online-only payments, this work operates under the the
+following theme: "If Taler can support offline payments that are no worse than
+those of competing systems (that often offer less freedom and privacy to
+users), why should we claim we can't support them and fare worse in comparisons"?
+
+Requirements
+============
+
+TBD.
+
+Possible Solutions
+==================
+
+Approach 1: Trust-based offline payments
+----------------------------------------
+
+The merchant simply trusts payments without depositing them at the exchange, up
+to a certain threshold and when an emergency mode is activated.
+
+Advantages:
+
+- Offers the most user freedom
+- Very few protocol/implementation changes required
+
+Disadvantages:
+
+- Requires manual switching to "emergency mode" where merchant
+ just trusts payments without verification.
+- Linkability of transactions unavoidable, because refresh is not available offline
+- Unclear if/how business logic based on order state will be affected.
+ (Will there be a "paid-offline" / "paid-unconfirmed" state for orders?)
+
+Approach 2: Full HSM wallet
+---------------------------
+
+Implement the full wallet in a hardware security module. When paying, the HSM
+wallet attests itself to the merchant, who then (if configured appropriately)
+trusts that the coins are still valid without talking to the exchange first.
+
+Advantages:
+
+- Easy to lift coins out of the HSM into a software wallet.
+ The HSM would internally mark the coins as "online" and give
+ the coin secrets to the software wallet.
+- Few exchange/merchant protocol changes required.
+
+Disadvantages:
+
+- Complex implementation, might not be supported on commodity HSMs.
+- Requires medium-to-major merchant backend changes (to verify HSM sigs and
+ deposit coins once offline)
+- Difficult to support P2P payments
+- Depending on available coins, linkable transactions
+ might be unavoidable.
+- Since the HSM wallet supports the full protocol, regulators might
+ be encouraged to make the HSM a requirement for *all* payments.
+- The usual (justified!) HSM security and freedom concerns
+
+Approach 3: Light-weight HSM balance register
+---------------------------------------------
+
+The HSM maintains an offline balance, effectively as a single, HSM protected
+register. To obtain offline cash, the software wallet spends coins to get a
+signature that can be passed to the HSM to increment the offline balance.
+To spend, the software wallet requests an "offline deposit permission"
+that decrements the HSM balance register. The exchange accepts these offline
+deposit permissions in lieu of a normal coin deposit permission.
+
+Advantages:
+
+- Trivially supports P2P payments
+- Cleanly separates normal Taler functionality from offline functionality
+
+Disadvantages:
+
+- Requires non-trivial (but backwards compatible) protocol changes
+- The usual (justified!) HSM security and freedom concerns
+
+
+Discussion / Q&A
+================
+
+(This should be filled in with results from discussions on mailing lists / personal communication.)
diff --git a/design-documents/031-invoicing.rst b/design-documents/031-invoicing.rst
new file mode 100644
index 00000000..419c299d
--- /dev/null
+++ b/design-documents/031-invoicing.rst
@@ -0,0 +1,202 @@
+DD31: Invoicing
+###############
+
+Summary
+=======
+
+This document proposes new endpoints to support invoicing,
+that incidentally also address the long-standing tipping
+reserve expiration problem.
+
+
+Motivation
+==========
+
+We want to support a limited number of PULL payment requests where a purse is
+created for a reserve without immediately requiring a purse fee. However, we
+must prevent users from excessively creating purses (and uploading contracts)
+as we do not want the exchange to be abused as a storage layer. Furthermore,
+it would be good if the user sending a PULL payment request was properly
+identified to the payer.
+
+This design addresses bugs #7269 and #7274.
+
+
+Requirements
+============
+
+ * Effectively limit the number of open purses created by each individual
+ (or require purse fees if limit is exceeded).
+
+ * Ensure user has done KYC before doing a merge.
+ (Assuming the exchange does KYC at all.)
+
+ * Use information from KYC process to help payer identify payee.
+
+ * Reasonable UX and overall design impact.
+
+ * Wallets may want to pay for the reserve with coins
+ (reserve fresh, not created via bank transfer), while
+ tipping merchants likely want to pay from the reserve
+ balance itself. So both styles of payment should be
+ supported.
+
+Unclear in the current proposal are:
+
+ * Here (and in other places!), the payment of the KYC
+ fee remains, eh, obscure. This should probably be
+ part of the KYC endpoints, and not for each
+ KYC-trigger.
+
+ * Proposed table structure does not properly capture
+ if user paid extra for more purses (I could open
+ for 3 years, then pay for 5x purses in year 1, but
+ should not automatically get 5x purses in years 2/3).
+
+
+Proposed Solution
+=================
+
+Allow users to tie their identity to a reserve "on demand" and when doing so
+charge the ``account_fee``, bump the number of open purses threshold in the
+``reserves`` table and stop auto-closing of the reserve. This will ensure that
+the users can withdraw the reserve balance into their wallet even after a
+longer time period. This helps if the invoice is paid after a significant
+delay, and also addresses the unwanted tipping reserve closure
+problem. Introduce a way to force an immediate closure of a reserve, allowing
+P2P reserve from invoices to be send to a bank account (this allows a wallet
+to be used for convenient invoicing and not strictly require the wallet to
+receive the funds) and also allowing the user to recover funds from a tipping
+reserve after tips are no longer issued.
+
+The solution needs three new tables for:
+
+ * account creation data:
+
+ - serial
+ - timestamp
+ - signature affirming desire to create account
+ - KYC requirement row
+
+ * account creation payment data:
+
+ - serial (for replication)
+ - coin signature (affirming payment)
+ - amount contributed
+ - account creation link (serial row ID)
+
+ * reserve closure request data:
+
+ - serial (for replication)
+ - timestamp
+ - reserve signature
+ - target account payto:// URI
+
+
+Specifically, the solution involves three new endpoints:
+
+Opening reserves
+----------------
+
+ * This new endpoint ``/reserve/$RID/open`` allows the user to
+ pay (for a year) to create a fixed number of purses and
+ to keep the reserve ``open`` (preventing auto-close); the
+ endpoint typically triggers a first (balance-independent)
+ KYC process (451) for a new KYC operation ``invoicing``
+ (unless KYC is off).
+
+ * Upon completion of the ``invoicing`` KYC, the wallet
+ must again try to ``/open``. If successful, the wallet
+ may be asked to pay the annual fee (402). However,
+ usually the wallet should be aware of the fee, and already
+ have included a suitable deposit in the POST to the endpoint.
+
+ * Once the annual fee is paid, the now open
+ reserve is set to a non-zero counter of allowed concurrently
+ open purses, and the expiration time of the reserve is bumped
+ to the end of the time period for which the fee was paid.
+
+Reserve Attestation
+-------------------
+
+ * This new endpoint ``/reserve/$RID/attest`` allows the user to
+ obtain exchange-signed KYC information about themselves.
+ This will basically be a list of (GANA standardized) attributes
+ and exchange signatures. The user can then choose which of
+ these attributes to include when invoicing. The available
+ set of attributes may differ depending on the KYC providers
+ configured and the attributes returned by the KYC logic.
+ We may choose to not use any fancy cryptography here, and
+ simply sign the different attributes individually. However,
+ we should always sign over the ``$RID`` to ensure that the
+ resulting signatures are meaningful.
+
+ * When receiving an invoice (PULL payment request), we may want to
+ mandate a certain minimal set of attributes that *should* always
+ be included, and if that is absent warn the receiver that the
+ sender of the invoice did not properly identify themselves.
+
+ * By making this a new endpoint, the client can re-request
+ the signatures on-demand. This is useful if we use the
+ EdDSA online signatures of the exchange, which likely expire
+ long before the user changes their attributes.
+
+
+Closing reserves
+----------------
+
+ * This new endpoint ``/reserve/$RID/close`` allows the user to
+ force-close a reserve that has not yet expired. This is useful
+ in case invoices have been paid into the reserve and the
+ user wants to get their money out. The ``close`` endpoint
+ must be provided with an appropriate payto://-URI as
+ reserves that were filled by P2P merge operations may not
+ already have an associated bank account (empty ``reserves_in``
+ table).
+
+
+Alternatives
+============
+
+We could require the target account to be already specified on ``open``.
+However, that prevents people from invoicing that have no account (or we'd
+have to allow ``payto://void/``, which then prevents people from closing later
+if they got a bank account at a later stage).
+
+We could allow an amount to be specified on ``close`` to partially wire the
+funds in a reserve. However, reserves are not supposed to be used as bank
+accounts (either to wallet *or* exceptionally to bank account, please!), and
+this would conflict with the current implementation of the
+**taler-exchange-closer**. So no amount is likely better for minimal
+regulatory and implementation trouble.
+
+Closing a reserve could also prevent the future use of the reserve for
+invoicing. Right now, the specification allows this to continue, effectively
+allowing users to repeatedly close an account to drain its funds to a bank
+account instead of into a wallet.
+
+We could mandate a fixed set of attributes. However, it is unclear whether all
+exchanges will always have KYC providers enabled that offer any particular set
+of attributes. It is conceivable that some exchanges may not run with any kind
+of KYC, or just with phone-number validation, while others may require
+government IDs but not phone numbers. So we can easily end up with completely
+disjunct sets of attributes across operators.
+
+We could not warn users if *insufficient* attributes were provided in an
+invoice. However, that seems dangerous, especially as fake invoices are a
+common attacker trick.
+
+We could use attribute-based credentials (ABC) for the attestations. Benefits
+and (complexity) drawbacks of such a change should be discussed with Martin.
+
+
+Drawbacks
+=========
+
+Quite a bit of work to implement.
+
+
+Discussion / Q&A
+================
+
+(This should be filled in with results from discussions on mailing lists / personal communication.)
diff --git a/design-documents/032-brandt-vickrey-auctions.rst b/design-documents/032-brandt-vickrey-auctions.rst
new file mode 100644
index 00000000..52a46cc0
--- /dev/null
+++ b/design-documents/032-brandt-vickrey-auctions.rst
@@ -0,0 +1,312 @@
+DD32: Brandt-Vickrey Auctions
+#############################
+
+Summary
+=======
+
+This document describes the design for how (SMC) auctions could be done with
+funds held in escrow by a Taler exchange. It is taking major inspiration from
+Markus Teich's Master's thesis on "Implementing Privacy Preserving Auction
+Protocols".
+
+The support for these types of auctions will be in the form of an extension for
+a deposit policy.
+
+
+Motivation
+==========
+
+Escrow is a key capability of payment systems. With SMC auctions we can
+broaden the escrow functionality beyond the (simplistic) refunds we currently
+offer. This provides a first new use-case for the extension mechanism. By
+using SMC auctions (libbrandt), we also provide privacy for another business
+process.
+
+We expect the design to be useful for three primary use-cases:
+
+ * Auctions in the style of Ebay (consumer goods)
+
+ * Auctions for commodities and stock trading
+
+ * Auctions in currency exchange, including crypto-currencies
+
+We do not consider the use-case of high-value art auctions, as here the Taler
+payment system is likely unsuitable for the high transaction values, the
+privacy for the buyer would still be problematic from a money-laundering
+perspective, and the cost of a trusted auctioneer manually running the auction
+is small compared to the transaction value, so the benefits from automation
+are also minor.
+
+
+Requirements
+============
+
+The discovery of ongoing auctions and the exchange of meta-data about the
+auction (duration, price ranges, conditions of the sale, etc.) is considered
+out-of-scope for the Taler protocol. Taler is only used to escrow a payment
+once participants have decided to make a bid on an aution.
+
+ * Support libbrandt-style SMC multiparty computation to determine
+ auction winner. No party is trusted to learn the bids during
+ the bidding phase, including the exchange. This implies that
+ participants have to always escrow the largest possible amount
+ in the auction, even if their actual bid is much lower.
+
+ * Integrate nicely with existing exchange functionality, including
+ minimal changes to existing endpoints and introducing as few new
+ endpoints as reasonable.
+
+ * Verifiablity of the auction outcome by the exchange and the
+ auditor. There is not supposed to be any trusted third party.
+ Naturally, when selling real-world goods, external enforcement
+ of the transfer of the good may be required.
+
+ * Guaranteed payment. If the auction is successful (there were at
+ least two bidders), the seller must unconditionally receive the
+ payment.
+
+ * Participants pay fees (possibly even just for participation),
+ to ensure only truly interested parties with skin in the game
+ participate (protection against denial-of-service attacks).
+
+ * To ensure participants can perform the required computations
+ in each round, the number of bidders on an auction may need
+ to be limited.
+
+
+
+Proposed Solution
+=================
+
+We will for now consider five types of parties involved in
+the auction:
+
+ * A seller, who is offering an item and who sets some basic
+ rules for the auction (like the price range, duration of
+ rounds, delivery conditions, seller's bank account,
+ auction operator, etc.).
+
+ * A number of bidders, who make bids on the auction, with
+ the highest bidder winnig the auction and paying the
+ second highest price to the seller.
+
+ * An auction operator, who collects messages from the seller
+ and the bidders and ultimately announces the (then universally
+ verifiable) outcome. In the original paper of Brand, this
+ would be the ``blackboard``. The auction operator may
+ also facilitate the discovery of auctions, but this is out
+ of scope. The auction operator may charge fees for setting
+ up and running an auction. However, from the Taler perspective,
+ paying an auction operator to run an auction is the same
+ as paying any other merchant and thus out of scope for this
+ design document.
+
+ * An exchange that supports the ``policy_vickrey_auction`` extension and
+ holds the funds for bids in escrow for the duration of the auction. Upon
+ completion of the auction, the exchange pays the seller from the auction's
+ winner and refunds the other bidders.
+
+ * An auditor that verifies that the exchange made the payments
+ correctly.
+
+The high-level protocol for a bidder's interaction with the auction operator
+and the Taler exchange is already described in Teich's thesis in Figure 3.2:
+the bidder begins by registering (say with an ephemeral EdDSA key) at the
+auction operator. Further messages from the bidder during this auction must
+then always be signed by the respective private key.
+
+The auction operator checks if there is a free slot (to limit the number of
+bidders per auction) and if one is free, gives the bidder a (modest) timeout
+until when it must prove that it escrowed an appropriate amount at the
+exchange. If no slots are free, the auction operator may allow the
+prospective bidder to long-poll for slots to become available (say because
+another prospective bidder failed to provide a proof of escrow on time).
+
+The bidder then uses the existing ``/deposit`` endpoint at the exchange to
+escrow the maximum bid. Escrowing the maximum bid ensures that no information
+about the actual bid is leaked to the exchange, and that any bid that could be
+made by the bidder can always be executed. In the ``/deposit``, the contract
+hash is set to information that includes those private parts of the auction
+meta data that do not concern the exchange (such as information about the item
+being sold). The seller's account information is included as the receiver of
+the funds. Additionally, the ``/deposit`` handler accepts an extension object
+which specifies the (SMC auction) extension and relevant meta-data about the
+auction (in particular, the bidder's ephemeral EdDSA public key, until when
+the auction runs, and (possibly) key material about the auction operator).
+
+The resulting proofs of deposits (plural, as there may be multiple coins
+involved) are then returned to the bidder. Note that the deposit confirmation
+signatures cover both the hash of the contract terms and the extension object.
+The deposit confirmations are then forwarded by the bidder to the auction
+operator, possibly already together with first (sealed) information about the
+bid.
+
+The auction operator then runs the auction protocol with all participants
+until conclusion. Once the winner and price have been determined, the auction
+operator POSTs the resulting transcript to a new
+``/extensions/policy_brandt_vickrey_auction`` endpoint of the exchange. Here,
+the extension-specific logic stores the transcript in its database (in a new
+table) and then simulates the auction again (using libbrandt), again
+determining the winner and price. The extension configuration (and thus
+``/keys``) may stipendulate some fee(s) charged by the exchange to handle the
+``/extensions/policy_brandt_vickrey_auction`` request. The fees should be
+covered by the seller. We note that the transcript inherently contains the
+deposit confirmations originally issued by the exchange for the auction. So,
+the exchange can identify all of the coins that were escrowed (it should also
+double-check that the coins were escrowed for the correct auction). It then
+refunds the bids from the loosing bidders, pays the price to the seller from
+the winner (minus auction fee), and partially refunds the winner the difference
+between the escrowed amount and the winning bid.
+
+ .. note::
+
+ Partial refunds are currently implemented using the ``refunds`` table.
+ The refunds table requires refund message signatures by the merchant's
+ public key. Thus, this table will need to be generalized to include
+ some indicator as to whether the refund signature is valid or
+ whether some other mechanism justified the refund. The most trivial
+ way would probably be to allow NULL values for the signature. However,
+ likely a link to the extension transcript should then be stored in
+ another column to make it easier for the auditor to look for
+ "alternative" justifications in those cases.
+
+In case participants are identified as malicious, the auction meta data should
+specify the penalty those participants must pay to the seller. Again, the
+exchange should assess the auction transcript and then trigger the correct
+transactions.
+
+The auditor of the exchange can again simulate the auction protocol and can
+thus confirm that the exchange's ultimate transactions were correct.
+
+Transcripts
+^^^^^^^^^^^
+
+A transcript of a Brandt-Vickrey auction is the JSON encoding of an object of
+type `BrandtVickreyAuctionTranscript`.
+
+ .. ts:def:: BrandtVickreyAuctionTranscript
+
+ // This structure defines the transcript of an auction of Brandt-Vickrey kind.
+ interface BrandtVickreyAuctionTranscript {
+ // The auction definition.
+ auction: BrandtVickreyAuction;
+
+ // The public keys of the bidders, in Crockford Base32 encoding.
+ bidders: EddsaPublicKey[];
+
+ // Signatures of the auction in Crockford Base32 encoding.
+ // One signature per bidder.
+ signatures: EddsaSignature[];
+
+ // List of policy hash codes that identify policy details associated with
+ // each bidder. Those codes were generated by the policy extension
+ // policy_brandt_vickrey_auction during the deposit of coins for this
+ // auction.
+ policy_hash_codes: HashCode[];
+
+ // The transcript of all messages received by the seller.
+ transcript: BrandtVickreyAuctionMessage[];
+
+ // Optionally, the seller can provide the winners it had calculated.
+ winners?: BrandtVickreyAuctionWinner[];
+
+ // The signature over the hash of this JSON object, without the
+ // key ``sig`` and in normalized form, basically over
+ // H(auction, bidders, signatures, transcripts, winners?)
+ // It is signed by the private key that corresponds to the public key
+ // in `BrandtVickreyAuction`.``pubkey``.
+ // This signature is in Crockford Base32 encoding.
+ sig: EddsaSignature;
+ }
+
+
+ .. ts:def:: BrandtVickreyAuctionMessage
+
+ interface BrandtVickreyAuctionMessage {
+ // The index of the bidder into the
+ // `BrandtVickreyAuctionTranscript`.``bidders`` array.
+ bidder: number;
+
+ // The raw message in Crockford Base32 encoding.
+ msg: string;
+
+ // The signature over the message. The signature is in Crockford Base32
+ // encoding. It must be signed by the private key corresponding to the
+ // bidder's public key in `BrandtVickreyAuctionTranscript`.``bidders``.
+ sig: EddsaSignature;
+ }
+
+
+
+ .. ts:def:: BrandtVickreyAuctionWinner
+
+ interface BrandtVickreyAuctionWinner {
+ // The index of the bidder into the
+ // `BrandtVickreyAuctionTranscript`.bidder array.
+ bidder: number;
+
+ // The index of the winning price into the
+ // `BrandtVickreyAuction`.prices array.
+ price_idx: number;
+
+ // The winning price
+ price: Amount;
+ }
+
+
+Alternatives
+============
+
+If currency is sold for currency in an auction, the seller could also escrow
+the currency being sold. This could be done by a simple parallel extension
+for sellers that provides the seller's escrow proof as input into the auction
+protocol. The result would effectively be an auction-driven equivalent of the
+atomic swap protocols for crytocurrencies.
+
+Instead of the exchange and the auditor re-running the auction protocol
+internally against the transcript, it might suffice if the auction operator,
+seller and all bidders jointly attest to the outcome. However, this presumes
+that there are no malicious participants. Thus, this is an optimization that
+can help, but likely should not be relied upon. The exchange may stipendulate
+different fees if auction participants provide signatures demonstrating that
+they agree upon the outcome of the auction.
+
+
+Drawbacks
+=========
+
+Forcing participants to escrow the largest possible bid may exclude some
+bidders. However, it can be assumed that the seller (wanting to get as many
+high bids as possible) will set a reasonable bidding range to not exclude
+realistic bids. If the seller set the bidding range wrong and receives no bids
+as a result, the auction can of course simply be repeated. Finally, excluding
+bidders that can only make rather low bids may help keep the number of
+participants managable. Given the three application domains we focus on,
+it seems that the number of bidders regularly excluded from the auction due
+to this constraint should be acceptable.
+
+
+Discussion / Q&A
+================
+
+A possible challenge that may require more thought is how to deal with auction
+participants dropping out and not sending any more messages and the
+equivalent attack from the auction operator of suppressing messages from
+certain participants. The latter case can likely be addressed partially by
+network-level anonymization of all participants, as then the auction operator
+doesn't have the ability to target specific users. However, a conspirator
+could still deanonymize themselves to the auctioneer with the objective of the
+auction operator then suppressing messages from other (anonymous)
+participants and thereby possibly excluding higher bids from those users.
+
+ .. note::
+
+ As described above, the Master's thesis of Markus Teich proposes to
+ address the issue of bidders dropping out of the protocol by fining them,
+ for example by keeping (some of) the escrowed funds. This may work, but
+ only if we assume that the auction operator is not maliciously dropping
+ messages from some bidders.
+
+
+
+(This should be filled in with results from discussions on mailing lists / personal communication.)
diff --git a/design-documents/033-database.rst b/design-documents/033-database.rst
new file mode 100644
index 00000000..ec91f6dc
--- /dev/null
+++ b/design-documents/033-database.rst
@@ -0,0 +1,152 @@
+Database Schema and Versioning
+##############################
+
+Summary
+=======
+
+This document describes how we version database schema in GNU Taler
+and enable migrations.
+
+
+Motivation
+==========
+
+As Taler evolves, it will be necessary to add new tables, change existing
+tables, modify indices and make other changes to the database schema. As
+production systems exist, existing data will need to be migrated to the new
+schema, and we need to do this in a systematic way. While certain major
+changes may require radical or manual interventions, we should have a
+systematic way of dealing with minor or modest modifications to the schema.
+
+
+Requirements
+============
+
+* The approach should be language-independent
+* Schema migration should be reasonably performant
+* Schema migration must be usable, and in particular safe to use for
+ operators without significant risk
+* We need to support key database features we might need to use,
+ such as partitioning or sharding
+
+
+Proposed Solution
+=================
+
+We use the "versioning.sql" system to store the current set of patches that
+have been applied to the database so far in a "_v" schema. This allows us to
+quickly test which version of the database we are on and which migrations may
+still need to be applied.
+
+For each component, all tables are placed into a SCHEMA named after the
+component.
+
+We then use a set of numbered SQL files that create, alter or drop tables and
+indices (like exchange-0001.sql, exchange-0002.sql, ...) to setup the
+database. However, some setups need additional arguments, such as the number
+of partitions. Those setups are then NOT performed explicitly, but by creating
+stored procedures and registering those stored procedures in a general global
+"master" table to be called from the main setup logic with arguments in a
+particular order under certain conditions.
+
+When setting up a database, there is no point in incrementally defining
+ordinary stored procedures that are used at runtime (not the ones to setup the
+tables we talked about above). Thus, all of the stored procedures used by the
+runtime system are placed in a file "procedures.sql" which is loaded
+last. This makes changes to stored procedures particularly easy, as one simply
+edits "procedures.sql". Loading "procedures.sql" also does not change "_v".
+
+A "drop.sql" file is created that DROPs the main SCHEMA of the component and
+additionally unregisters all patches from the "_v" schema. The drop script
+is run during tests to fully reset the database.
+
+Exchange details
+^^^^^^^^^^^^^^^^
+
+The exchange uses "exchange_tables" to create the master
+table mentioned above. In "exchange_tables", entries are
+executed in the order of the "table_serial_id". Each
+entry has a "name", which is the name of the affected table
+(or at least the prefix in the case of partitioned or sharded
+tables). The "version" field stores which "exchange-XXXX.sql"
+file setup the respective table entry, but is for now mostly
+for internal documentation. The "action" defines both the
+condition under which to run a function. Specifically,
+actions can be:
+
+* create --- run on the master table and each shard; used to create or alter the main table
+* constrain --- run only on the partitions/shards, or on master if there are no partitions; used to setup constraints like uniqueness that only apply to the lowest levels of the table
+* master -- run only on the master table; used to setup triggers and other constraints that only apply to the master table
+* foreign -- run only on the master table and only if there are no partition; used to setup foreign key constraints that are not supported on partitioned or sharded tables
+
+The "partitioned" field indicates that this table is partitioned and instructs the functions to create partitions (or shards)
+for this table.
+
+The "by_range" field indicates if the table is partitioned by
+range, which prevents automatic generation of partitions as
+is done if partitioned by hash.
+
+The "finished" field is initially false, but set to TRUE once the respective
+function has been executed.
+
+The main "do_create_tables" function triggers the unfinished actions
+registered in the "exchange_tables" table. It is given arguments to control
+the number of partitions, the use of partitions and (in the future) the use of
+sharding.
+
+The individual actions use helper functions ("create_partitioned_table",
+"comment_partitioned_table" and "comment_partitioned_column") to facilitate
+the creation of tables and associated comments. These functions are used so
+that we can only define the schema or comment once, and have it applied to
+tables with names and creation syntax that changes slightly if we use shards
+or partitions.
+
+Some additional logic will be needed to deal nicely with
+sharding (which is currently not supported), as with
+sharing we will need to detach shards, migrate shards, and
+re-attach shards. So this will require additional stored
+procedures to support operations on shards.
+
+
+Merchant details
+^^^^^^^^^^^^^^^^
+
+The merchant does not (yet) need any type of master table, as we do not
+(yet) use any kind of sharding or partitioning. There are also no
+stored procedures being used by the backend. Hence, it is simply the
+"versioning.sql"-controlled table creation/alteration sequence
+(merchant-0001.sql, etc.) and the "drop.sql" to reset everything.
+
+
+Alternatives
+============
+
+* We might want to consider storing more meta-data
+ in the database, such as the use of sharding, the
+ names of the shard servers, or even just the number
+ of partitions.
+
+* We could require dumping out the old database and
+ loading it in via some explicit importer during each
+ migration; having migration logic in C would enable more
+ powerful migrations, but dumping and reloading the entire
+ database would also be more expensive. It would have the
+ advantage of basically having the old database around in
+ case of migration trouble, so the cost disadvantage might
+ not be so bad (as admins are likely to make a backup anyway).
+ OTOH, doing the migration with the equivalent of a
+ taler-auditor-sync would require quite a bit more code
+ than the simple ALTER/CREATE statements in an SQL file.
+
+
+Drawbacks
+=========
+
+* not exactly trival logic
+* some complexity to implement
+
+
+Discussion / Q&A
+================
+
+(This should be filled in with results from discussions on mailing lists / personal communication.)
diff --git a/design-documents/_svgs/escrow-flow.svg b/design-documents/_svgs/escrow-flow.svg
new file mode 100644
index 00000000..6fc0a51f
--- /dev/null
+++ b/design-documents/_svgs/escrow-flow.svg
@@ -0,0 +1 @@
+<svg aria-labelledby="chart-title-graph-div chart-desc-graph-div" role="img" viewBox="-111 -10 1273 1070" style="max-width: 1273px;" height="1070" xmlns="http://www.w3.org/2000/svg" width="100%" id="graph-div"><title id="chart-title-graph-div"></title><desc id="chart-desc-graph-div"></desc><style>#graph-div {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#graph-div .error-icon{fill:#552222;}#graph-div .error-text{fill:#552222;stroke:#552222;}#graph-div .edge-thickness-normal{stroke-width:2px;}#graph-div .edge-thickness-thick{stroke-width:3.5px;}#graph-div .edge-pattern-solid{stroke-dasharray:0;}#graph-div .edge-pattern-dashed{stroke-dasharray:3;}#graph-div .edge-pattern-dotted{stroke-dasharray:2;}#graph-div .marker{fill:#333333;333;}#graph-div .marker.cross{333;}#graph-div svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#graph-div .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#graph-div text.actor&gt;tspan{fill:black;stroke:none;}#graph-div .actor-line{stroke:grey;}#graph-div .messageLine0{stroke-width:1.5;stroke-dasharray:none;;}#graph-div .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;;}#graph-div #arrowhead path{fill:#333;;}#graph-div .sequenceNumber{fill:white;}#graph-div #sequencenumber{fill:#333;}#graph-div #crosshead path{fill:#333;;}#graph-div .messageText{fill:#333;;}#graph-div .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#graph-div .labelText,#graph-div .labelText&gt;tspan{fill:black;stroke:none;}#graph-div .loopText,#graph-div .loopText&gt;tspan{fill:black;stroke:none;}#graph-div .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#graph-div .note{stroke:#aaaa33;fill:#fff5ad;}#graph-div .noteText,#graph-div .noteText&gt;tspan{fill:black;stroke:none;}#graph-div .activation0{fill:#f4f4f4;stroke:#666;}#graph-div .activation1{fill:#f4f4f4;stroke:#666;}#graph-div .activation2{fill:#f4f4f4;stroke:#666;}#graph-div .actorPopupMenu{position:absolute;}#graph-div .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#graph-div .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#graph-div .actor-man circle,#graph-div line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#graph-div :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style><g></g><defs><symbol height="24" width="24" id="computer"><path d="M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z" transform="scale(.5)"></path></symbol></defs><defs><symbol clip-rule="evenodd" fill-rule="evenodd" id="database"><path d="M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z" transform="scale(.5)"></path></symbol></defs><defs><symbol height="24" width="24" id="clock"><path d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z" transform="scale(.5)"></path></symbol></defs><g><line stroke="#999" stroke-width="0.5px" class="200" y2="1004" x2="75" y1="5" x1="75" id="actor6934"></line><g id="root-6934"><rect class="actor" ry="3" rx="3" height="65" width="150" stroke="#666" fill="#eaeaea" y="0" x="0"></rect><text style="text-anchor: middle; font-size: 14px; font-weight: 400; font-family: &quot;Open Sans&quot;, sans-serif;" class="actor" alignment-baseline="central" dominant-baseline="central" y="32.5" x="75"><tspan dy="0" x="75">Seller</tspan></text></g></g><g><line stroke="#999" stroke-width="0.5px" class="200" y2="1004" x2="575" y1="5" x1="575" id="actor6935"></line><g id="root-6935"><rect class="actor" ry="3" rx="3" height="65" width="150" stroke="#666" fill="#eaeaea" y="0" x="500"></rect><text style="text-anchor: middle; font-size: 14px; font-weight: 400; font-family: &quot;Open Sans&quot;, sans-serif;" class="actor" alignment-baseline="central" dominant-baseline="central" y="32.5" x="575"><tspan dy="0" x="575">Exchange</tspan></text></g></g><g><line stroke="#999" stroke-width="0.5px" class="200" y2="1004" x2="949" y1="5" x1="949" id="actor6936"></line><g id="root-6936"><rect class="actor" ry="3" rx="3" height="65" width="150" stroke="#666" fill="#eaeaea" y="0" x="874"></rect><text style="text-anchor: middle; font-size: 14px; font-weight: 400; font-family: &quot;Open Sans&quot;, sans-serif;" class="actor" alignment-baseline="central" dominant-baseline="central" y="32.5" x="949"><tspan dy="0" x="949">Buyer</tspan></text></g></g><defs><marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="9" id="arrowhead"><path d="M 0 0 L 10 5 L 0 10 z"></path></marker></defs><defs><marker refY="4" refX="16" orient="auto" markerHeight="8" markerWidth="15" id="crosshead"><path style="stroke-dasharray: 0, 0;" d="M 9,2 V 6 L16,4 Z" stroke-width="1px" stroke="#000000" fill="black"></path><path style="stroke-dasharray: 0, 0;" d="M 0,1 L 6,7 M 6,1 L 0,7" stroke-width="1px" stroke="#000000" fill="none"></path></marker></defs><defs><marker orient="auto" markerHeight="28" markerWidth="20" refY="7" refX="18" id="filled-head"><path d="M 18,7 L9,13 L14,7 L9,1 Z"></path></marker></defs><defs><marker orient="auto" markerHeight="40" markerWidth="60" refY="15" refX="15" id="sequencenumber"><circle r="6" cy="15" cx="15"></circle></marker></defs><g><rect class="note" ry="0" rx="0" height="52" width="272" stroke="#666" fill="#EDF2AE" y="75" x="-61"></rect><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="80" x="75"><tspan x="75">The seller creates an escrow account </tspan></text><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="96" x="75"><tspan x="75"> with specific terms and a public key EID</tspan></text></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="142" x="325">register escrow (EID, [m, M], starttime, endtime)</text><line style="fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine0" y2="175" x2="575" y1="175" x1="75"></line><g><rect class="activation0" ry="0" rx="0" height="48" width="10" stroke="#666" fill="#EDF2AE" y="175" x="570"></rect></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="190" x="323">(EID + sig_e(EID, [m, M], starttime, endtime))</text><line style="fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine0" y2="223" x2="75" y1="223" x1="570"></line><g><rect class="note" ry="0" rx="0" height="52" width="188" stroke="#666" fill="#EDF2AE" y="233" x="-19"></rect><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="238" x="75"><tspan x="75">Seller publishes the EID, </tspan></text><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="254" x="75"><tspan x="75"> for example for an auction</tspan></text></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="300" x="512">publish auction (EID, [m, M], starttime, endtime, sig)</text><line style="stroke-dasharray: 3, 3; fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine1" y2="333" x2="949" y1="333" x1="75"></line><g><rect class="note" ry="0" rx="0" height="53" width="326" stroke="#666" fill="#EDF2AE" y="343" x="786"></rect><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="348" x="949"><tspan x="949">An interested bidder can receive the list of</tspan></text><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="364" x="949"><tspan x="949"> depositors (not amounts) for the escrow account</tspan></text></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="411" x="762">list(EID)</text><line style="fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine0" y2="444" x2="575" y1="444" x1="949"></line><g><rect class="activation0" ry="0" rx="0" height="48" width="10" stroke="#666" fill="#EDF2AE" y="444" x="570"></rect></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="459" x="765">list of depositors</text><line style="fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine0" y2="492" x2="949" y1="492" x1="580"></line><g><rect class="note" ry="0" rx="0" height="52" width="315" stroke="#666" fill="#EDF2AE" y="502" x="791.5"></rect><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="507" x="949"><tspan x="949">A interested bidder can deposit coins into the </tspan></text><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="523" x="949"><tspan x="949">escrow account and receives a proof of escrow</tspan></text></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="569" x="762">deposit(EID, amount, coins[], DEPOSIT_ID)</text><line style="fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine0" y2="602" x2="575" y1="602" x1="949"></line><g><rect class="activation0" ry="0" rx="0" height="48" width="10" stroke="#666" fill="#EDF2AE" y="602" x="570"></rect></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="617" x="765">proof (EID, DEPOSIT_ID, [m,M])</text><line style="fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine0" y2="650" x2="949" y1="650" x1="580"></line><g><rect class="note" ry="0" rx="0" height="52" width="233" stroke="#666" fill="#EDF2AE" y="660" x="832.5"></rect><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="665" x="949"><tspan x="949">The bidder can now use the proof</tspan></text><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="681" x="949"><tspan x="949"> to participate in the auction</tspan></text></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="727" x="512">bid (EID, proof(EID, [m,M]))</text><line style="stroke-dasharray: 3, 3; fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine1" y2="760" x2="75" y1="760" x1="949"></line><g><rect class="note" ry="0" rx="0" height="52" width="299" stroke="#666" fill="#EDF2AE" y="770" x="799.5"></rect><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="775" x="949"><tspan x="949">The winning bidder provides a release token </tspan></text><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="791" x="949"><tspan x="949"> for the winning amount to the seller</tspan></text></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="837" x="512">EID, DEPOSIT_ID, amount, coins[], sig_b(...)</text><line style="fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine0" y2="870" x2="75" y1="870" x1="949"></line><g><rect class="note" ry="0" rx="0" height="36" width="208" stroke="#666" fill="#EDF2AE" y="880" x="-29"></rect><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 14px; font-weight: 400;" dy="1em" class="noteText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="885" x="75"><tspan x="75">The seller can claim the coins</tspan></text></g><text style="font-family: &quot;trebuchet ms&quot;, verdana, arial, sans-serif; font-size: 16px; font-weight: 400;" dy="1em" class="messageText" alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" y="931" x="325">claim(EID, DEPOSIT_ID, amount, coins[], sig_b, sig_s(sig_b))</text><line style="fill: none;" marker-end="url(#arrowhead)" stroke="none" stroke-width="2" class="messageLine0" y2="964" x2="575" y1="964" x1="75"></line><g><rect class="actor" ry="3" rx="3" height="65" width="150" stroke="#666" fill="#eaeaea" y="984" x="0"></rect><text style="text-anchor: middle; font-size: 14px; font-weight: 400; font-family: &quot;Open Sans&quot;, sans-serif;" class="actor" alignment-baseline="central" dominant-baseline="central" y="1016.5" x="75"><tspan dy="0" x="75">Seller</tspan></text></g><g><rect class="actor" ry="3" rx="3" height="65" width="150" stroke="#666" fill="#eaeaea" y="984" x="500"></rect><text style="text-anchor: middle; font-size: 14px; font-weight: 400; font-family: &quot;Open Sans&quot;, sans-serif;" class="actor" alignment-baseline="central" dominant-baseline="central" y="1016.5" x="575"><tspan dy="0" x="575">Exchange</tspan></text></g><g><rect class="actor" ry="3" rx="3" height="65" width="150" stroke="#666" fill="#eaeaea" y="984" x="874"></rect><text style="text-anchor: middle; font-size: 14px; font-weight: 400; font-family: &quot;Open Sans&quot;, sans-serif;" class="actor" alignment-baseline="central" dominant-baseline="central" y="1016.5" x="949"><tspan dy="0" x="949">Buyer</tspan></text></g></svg>
diff --git a/design-documents/index.rst b/design-documents/index.rst
index 7de0d2ea..4afb7280 100644
--- a/design-documents/index.rst
+++ b/design-documents/index.rst
@@ -33,4 +33,13 @@ and protocol.
022-wallet-auditor-reports
023-taler-kyc
024-age-restriction
+ 025-withdraw-from-wallet
+ 026-refund-fees
+ 027-sandboxing-taler.rst
+ 028-deposit-policies
+ 029-mobile-ui
+ 030-offline-payments
+ 031-invoicing
+ 032-brandt-vickrey-auctions
+ 033-database
999-template