commit 30953a942d17a03abf4e5061b2bcbabc357bbd7d
parent 60cc468cad2b18d8c7ec23ae648747b5dfc47e08
Author: Christian Grothoff <christian@grothoff.org>
Date: Sat, 17 Oct 2026 15:51:42 +0200
new /keys fields for v42 to bound purse expiration as needed for auditor
Diffstat:
7 files changed, 77 insertions(+), 5 deletions(-)
diff --git a/core/api-exchange.rst b/core/api-exchange.rst
@@ -70,7 +70,13 @@ The currently implemented protocol version is **v42**.
adds the required ``reserve_pub`` field to the
:http:post:`/reveal-withdraw </reveal-withdraw>` request body,
so that a withdraw operation is identified by the pair
- (reserve, commitment) everywhere
+ (reserve, commitment) everywhere;
+ bounds ``purse_expiration`` by the new ``max_p2p_expiration``
+ of :http:get:`/keys </keys>` and by the legal expiration of the
+ denominations of the coins in the purse, with the new error
+ codes ``TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_TOO_LATE``,
+ ``TALER_EC_EXCHANGE_RESERVES_PURSE_EXPIRATION_TOO_LATE`` and
+ ``TALER_EC_EXCHANGE_PURSE_DENOMINATION_LEGAL_EXPIRES_TOO_SOON``
**Upcoming versions:**
diff --git a/core/auditor/get-monitoring-historic-denomination-revenue.rst b/core/auditor/get-monitoring-historic-denomination-revenue.rst
@@ -2,6 +2,12 @@
Get a list of historic denomination revenue stored by the auditor.
+ A denomination is booked here once its legal period is over, so that the
+ entry is final. There can nonetheless be more than one entry per
+ denomination: if the exchange moves value on a denomination that was
+ already booked, the auditor appends the additional revenue as another
+ entry instead of changing the one it already published.
+
The following query parameters are optional, and can be used to customise the response:
**Request:**
@@ -50,8 +56,9 @@
// Hash code of the denomination public key involved
h_denom_pub : HashCode;
- // Time when the denomination expired and thus the revenue
- // was computed.
+ // Time when the denomination expired for deposits. Note that
+ // the revenue is only booked once the legal period of the
+ // denomination is over as well.
revenue_timestamp : Timestamp;
// Total fee revenue the exchange earned from coins of this
diff --git a/core/exchange/get-keys.rst b/core/exchange/get-keys.rst
@@ -165,6 +165,16 @@
// @since protocol **v35**.
default_p2p_push_expiration: RelativeTime;
+ // How far in the future may the ``purse_expiration`` of a purse be?
+ // An expiring purse refunds the coins deposited into it, and the
+ // exchange must still be able to account for that refund, so a purse
+ // may not outlive the denominations of the coins in it. Applies to
+ // both purse-creating endpoints, and in addition no coin may be
+ // deposited into a purse that would outlive the
+ // ``stamp_expire_legal`` of the coin's own denomination.
+ // @since protocol **v42**.
+ max_p2p_expiration: RelativeTime;
+
// Threshold amounts beyond which wallet should
// trigger the KYC process of the issuing exchange.
// Optional option, if not given there is no limit.
diff --git a/core/exchange/post-purses-PURSE_PUB-create.rst b/core/exchange/post-purses-PURSE_PUB-create.rst
@@ -6,6 +6,13 @@
The request body must be a `PurseCreateRequest` object.
+ A purse that is never merged refunds its coins when it expires, and the
+ exchange must still be able to account for that refund.
+ ``purse_expiration`` is therefore limited to ``max_p2p_expiration`` (see
+ :http:get:`/keys </keys>`) from now, and in addition no coin may be
+ deposited into a purse that outlives the ``stamp_expire_legal`` of the
+ coin's own denomination.
+
**Response:**
:http:statuscode:`200 OK`:
@@ -17,8 +24,12 @@
This response comes with a standard `ErrorDetail` response.
Possible error codes include ``TALER_EC_GENERIC_PARAMETER_MALFORMED``,
``TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_BEFORE_NOW``,
- ``TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_IS_NEVER``, or
+ ``TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_IS_NEVER``,
+ ``TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_TOO_LATE``,
+ ``TALER_EC_EXCHANGE_PURSE_DENOMINATION_LEGAL_EXPIRES_TOO_SOON``, or
``TALER_EC_EXCHANGE_CREATE_PURSE_NEGATIVE_VALUE_AFTER_FEE``.
+ For the two expiration errors the ``detail`` of the `ErrorDetail`
+ gives the latest purse expiration that would have been accepted.
:http:statuscode:`403 Forbidden`:
A coin, denomination or contract signature is invalid.
This response comes with a standard `ErrorDetail` response.
@@ -111,6 +122,8 @@
// Indicative time by which the purse should expire
// if it has not been merged into an account. At this
// point, all of the deposits made will be auto-refunded.
+ // Must not be further in the future than
+ // ``max_p2p_expiration`` of :http:get:`/keys </keys>`.
purse_expiration: Timestamp;
}
diff --git a/core/exchange/post-purses-PURSE_PUB-deposit.rst b/core/exchange/post-purses-PURSE_PUB-deposit.rst
@@ -6,6 +6,11 @@
The request body must be a `PurseDeposits` object.
+ A purse that is not decided refunds the coins deposited into it when it
+ expires, and the exchange must still be able to account for that refund.
+ A coin whose denomination would stop being legally valid before the
+ purse expires is therefore refused.
+
**Response:**
:http:statuscode:`200 OK`:
@@ -15,7 +20,10 @@
:http:statuscode:`400 Bad Request`:
The request body is malformed or a parameter is invalid.
This response comes with a standard `ErrorDetail` response with
- a code of ``TALER_EC_GENERIC_PARAMETER_MALFORMED``.
+ a code of ``TALER_EC_GENERIC_PARAMETER_MALFORMED`` or of
+ ``TALER_EC_EXCHANGE_PURSE_DENOMINATION_LEGAL_EXPIRES_TOO_SOON``,
+ whose ``detail`` gives the ``stamp_expire_legal`` of the denomination
+ that is too short-lived for this purse.
:http:statuscode:`403 Forbidden`:
A coin or denomination signature is invalid.
This response comes with a standard `ErrorDetail` response.
diff --git a/core/exchange/post-reserves-RESERVE_PUB-purse.rst b/core/exchange/post-reserves-RESERVE_PUB-purse.rst
@@ -6,6 +6,11 @@
The request body must be a `ReservePurseRequest` object.
+ A purse that is not paid in full refunds the coins that were deposited
+ into it when it expires, and the exchange must still be able to account
+ for that refund. ``purse_expiration`` is therefore limited to
+ ``max_p2p_expiration`` (see :http:get:`/keys </keys>`) from now.
+
**Response:**
:http:statuscode:`200 OK`:
@@ -24,6 +29,10 @@
A purse expiration time of "never" is not allowed.
4. ``TALER_EC_EXCHANGE_RESERVES_PURSE_FEE_TOO_LOW``:
The specified purse fee is lower than allowed.
+ 5. ``TALER_EC_EXCHANGE_RESERVES_PURSE_EXPIRATION_TOO_LATE``:
+ The requested purse expiration time is further in the future than
+ ``max_p2p_expiration`` allows. The ``detail`` of the `ErrorDetail`
+ gives the latest purse expiration the exchange accepts.
:http:statuscode:`403 Forbidden`:
Account or contract signature is invalid.
diff --git a/taler-exchange-manual.rst b/taler-exchange-manual.rst
@@ -684,6 +684,18 @@ Payment data is deleted whenever the legal expiration date for the
``DURATION_LEGAL`` configured in the respective section ``coin`` is reached
(typically also after 10 years).
+Because a purse refunds the coins deposited into it when it expires, and
+the auditor has to be able to account for that refund, the expiration of
+a purse is bounded by ``PURSE_EXPIRATION_LIMIT`` in the ``[exchange]``
+section. It must not exceed the shortest ``DURATION_LEGAL`` of any
+denomination and is published to clients in ``/keys``; if the option is
+not given, the exchange uses that shortest ``DURATION_LEGAL`` and logs a
+warning. Lowering ``DURATION_LEGAL`` by some span X with effect from
+time T therefore requires lowering ``PURSE_EXPIRATION_LIMIT`` by X at
+T-X, so that the purses created before the change are short enough for
+the denominations issued after it; otherwise deposits of the new coins
+into those older purses are refused.
+
Basic Setup: Currency, Denominations and Keys
=============================================
@@ -733,6 +745,13 @@ must then have the following options:
values result in lower storage costs for the exchange.
- ``DURATION_LEGAL``: How long is the coin of the given type legal?
+ ``DURATION_WITHDRAW`` plus ``DURATION_SPEND`` plus ``DURATION_LEGAL``
+ must not exceed ``LEGAL_RESERVE_EXPIRATION_TIME`` of the
+ ``[exchangedb]`` section, as otherwise the garbage collection could
+ drop the denomination before the auditor is done accounting for it.
+ ``DURATION_LEGAL`` must also be at least ``PURSE_EXPIRATION_LIMIT``
+ of the ``[exchange]`` section. The exchange refuses to start if
+ either is violated.
- ``FEE_WITHDRAW``: What does it cost to withdraw this coin? Specified
using the same format as value.