taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

commit 6d3cc0987366cdccf8ef4d2b3dd905cd868781bd
parent 763366771f81553fc0590efbb69aa8fe7dfcf857
Author: Florian Dold <florian@dold.me>
Date:   Tue,  8 Jul 2025 14:46:11 +0200

DD64: simplify and concretize steps

Diffstat:
Mdesign-documents/064-kyc-operation-algo.rst | 179++++++++++++++++++++++++++++++++++---------------------------------------------
1 file changed, 77 insertions(+), 102 deletions(-)

diff --git a/design-documents/064-kyc-operation-algo.rst b/design-documents/064-kyc-operation-algo.rst @@ -24,120 +24,95 @@ Requirements Proposed Solution ================= + Steps for processing operation of type ``op`` and amount ``amt``: -0. Initialize ``last_op_attempt := null``, ``last_rule_gen := null`` - and ``last_aml_review := false`` and all back-off values to 0. -1. Check if a zero limit for ``op`` is applicable. +Initialization +^^^^^^^^^^^^^^ + +Initialize the following variables: + +* ``last_check_status := null`` + + * Last HTTP status of the kyc-check request. + A status of ``0`` indicates a network failure or request timeout + and is distinct from ``null``, which indicates that no check + request has been made. + +* ``last_check_code := null`` + + * Taler error code of the last kyc-check request or ``null`` if no such + request has been made or the last request didn't return an error code. - * If the zero limit denies ``op``, proceed with step 3. - * Proceed with step 2. +* ``last_rule_gen := null`` +* ``last_aml_review := null`` +* ``last_op_deny := if isZeroLimited(op, amt) then now() else null`` +* ``account_keypair := getCurrentAccountKeyPair(op)`` -2. Attempt to make a request for ``op`` at the exchange. If the - ``/kyc-check/`` result did not change since the last attempt, - make sure to use exponential backoff on ``op`` requests with - the frequency increasing up to once per day. The exponential - backoff should be reset to 0 if the ``/kyc-check/`` result - changed in any way. - Set ``last_op_attempt`` to the current time. +Processing +^^^^^^^^^^ + +1. If ``last_op_deny`` is ``null`` or more than ``1h`` ago, + make a request for ``op`` at the exchange. Let ``resp`` be the response. * If the request succeeds, *halt*. - * If ``last_aml_review`` was true, proceed with step 6. - * If ``last_rule_gen`` is not ``null`` (Note 1), proceed with step 5. - * Proceed with step 3. - -3. Request the ``/kyc-check/...`` endpoint applicable for ``op`` - (without long-polling!). - - * Handle response as detailed in step 7. - -4. Request the ``/kyc-check/...`` endpoint applicable for ``op`` - with long-polling for ``lpt=1``. - - * While waiting for the response, prompt the user to - perform KYC auth wire transfer. - * Handle response as detailed in step 7. - -5. Request the ``/kyc-check/...`` endpoint applicable for ``op`` - with long-polling for ``min_rule`` set to ``last_rule_gen``. - - * While waiting for the response, prompt the user to - provide KYC information. - * Handle response as detailed in step 7. - -6. Request the ``/kyc-check/...`` endpoint applicable for ``op`` - with long-polling for ``lpt=2`` and, additionally - ``min_rule`` set to the last ``last_rule_gen`` (if the - latter is not ``null``). - - * While waiting, indicate to the user that we are waiting on the - provider's staff to review our account. - * Handle response as detailed in step 7. - -7. General handling of ``/kyc-check`` responses: - - * If the request returns ``409 Conflict``, proceed with step 4. - * If the request returns ``404 Not found``, proceed with - step 8 applying the exposed default rules of the exchange. - * If the request returns ``403 Forbidden``, check if the - private key for the indicated public key is available, if - so try again with that private key, otherwise - proceed with step 4. - * If the request returns ``204 No Content``: - * Set ``last_aml_review := false``. - * Proceed with step 2. - * If the request returns ``202 Accepted``: - - * Set ``last_aml_review := response.aml_review``. - * Set ``last_rule_gen := response.rule_gen``. - * If the exposed limits do not explicitly deny ``op``, - proceed with step 2. - * Otherwise, proceed with step 5. - - * If the request returns ``200 Ok``: - - * Set ``last_aml_review := response.aml_review``. - * Set ``last_rule_gen := response.rule_gen``. - * Handle exposed limits returned in step 8. - - * If the response indicates no change in the status (including - when there was no response because the connection was closed): - - * Repeat the request, using exponential back-off to reduce - check frequency (without even long-polling) to eventually - only check once per day. - * Ensure the long-polling interval specified in the request - works with the middleware. If we detect that a request timed - out before the specified long-polling interval, use a shorter - timeout argument in the HTTP request the next time - (but do still keep exponentially - backing off the actual request frequency). - * Lower the back-off to zero if: - - * the user manually reviews the KYC auth wire transfer instructions, or - * the user manually reviews KYC information page instructions, or - * if ``op`` is a deposit and ``lpt=1`` and a withdraw succeeded - from the same account - -8. Handle exposed limits applicable to the account: - - * If the exposed limits do not deny ``op``, proceed with step 2. - * Otherwise (the exposed limits do deny ``op``), show an error message + +2. Request the ``/kyc-check/...`` endpoint applicable for ``op`` with ``account_keypair`` the following parameters: + + * If ``last_check_status == null``: Make request without long-polling. + * If ``last_check_status in [403 Forbidden, 409 Conflict]``: Long-poll. Add query parameter ``lpt=1`` + * If ``last_aml_review == true``: Long-poll. Add query parameter ``lpt=2``. If ``last_rule_gen != null``, add + query parameter ``min_rule=last_rule_gen``. + * If ``last_check_status == 402 Accepted``: Long-poll. If ``last_rule_gen != null``, add ``min_rule=last_rule_gen`` + to the query parameters. + +3. Handle the ``/kyc-check/...`` response: + + * Set ``same_resp := resp.status == last_check_status and resp.code == last_check_status and resp.rule_gen == last_rule_gen``. + * Set ``last_check_status := resp.status``, ``last_check_code := resp.code``, ``last_rule_gen := resp.rule_gen`` + * If ``same_resp == true``: finish processing operation with result ``BACKOFF``. + * If ``resp.status == 204 No Content``: Set ``last_op_deny := null``. Finish processing operation with result ``PROGRESS`` (effectively + re-trying at step 1). + * If ``resp.status == 200 Ok``: Set ``last_op_deny := null``. Go to step 4. + * If ``resp.status == 202 Accepted``: Go to step 4. + * If ``resp.status == 403 Forbidden``: Check if the private key for the + indicated public key is available. If, set ``account_keypair`` to that key pair and finish with result ``PROGRESS``. + Otherwise, finish with result ``BACKOFF``. + * If ``resp.status == 404 Not Found``: Go to step 4, with exposed limits set to the default limits. + * Otherwise (unhandled status), finish processing operation with result ``BACKOFF``. + +4. Handle exposed limits applicable to the account: + + * If the exposed limits do not deny ``op``, finish processing operation with result ``PROGRESS``. + * Otherwise (the exposed limits do deny ``op``), transition the transaction + to a ``failed`` state (Showing an error message indicating that the operation is forbidden due to legal - restrictions on the payment service provider. + restrictions on the payment service provider). Finish processing operation with result ``PROGRESS``. * If merely a threshold over some timeframe is currently - violated, compute the time when the operation may be allowed again, - indicate that to the user, allowing them to "retry immediately" - anyway, and also go to step 2 automatically at that time. + violated, compute the time ``t`` when the operation may be allowed again. + Finish processing operation with result ``AGAIN_AT(t)``. + + +Additional Considerations +^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Ensure the long-polling interval specified in the request + works with the middleware. If we detect that a request timed + out before the specified long-polling interval, use a shorter + timeout argument in the HTTP request the next time + (but do still keep exponentially + backing off the actual request frequency). +* Lower the retry back-off for the transaction to zero if: -Notes: ------- + * the user manually reviews the KYC auth wire transfer instructions, or + * the user manually reviews KYC information page instructions, or + * if ``op`` is a deposit and ``lpt=1`` and a withdraw succeeded + from the same account -* Note 1: We must long-poll for the next rule generation here, - since we can assume that the current set of rules contains non-exposed - rule that prevents the current operation. +* Especially in step 4 (limit violated in timeframe), + the user should be offered the option to retry. Definition of Done