commit e1b71d87fc789796f3864c13395bb4260a314909
parent 66be441df4e6ad63924d4aba08bd81d66098f53c
Author: Özgür Kesim <oec-taler@kesim.org>
Date: Sun, 30 Nov 2025 19:47:48 +0100
[exchange][refresh] spec for /refresh according to Dold'19++
The current design for refresh requires _unique_ coin signatures,
which we do not have. We therefore revert to the Dold'19 refresh,
_but_ keep the improvements regarding batch-handling and idempotency.
Diffstat:
2 files changed, 89 insertions(+), 35 deletions(-)
diff --git a/core/api-common.rst b/core/api-common.rst
@@ -1197,8 +1197,6 @@ uses 512-bit hash codes (64 bytes).
struct TALER_TransferSecretP {
uint8_t key[sizeof (struct GNUNET_HashCode)];
};
- uint8_t key[sizeof (struct GNUNET_HashCode)];
- };
struct TALER_EncryptedLinkSecretP {
uint8_t enc[sizeof (struct TALER_LinkSecretP)];
@@ -1449,20 +1447,21 @@ within the
/**
* @since vDOLDPLUS
* Hash over:
- * 1. kappa*n tranfer public keys: T[1,1],⋯,T[1,n],T[2,1],⋯,T[κ,n]
- * 2. hash over all pairs of R-values (for CS) if present, skipped otherwise
- * 3. n denomination hashes, in order
- * 4. amount with fee
- * 5. kappa*n planchets, depths first: [0..n),[0..n),[0..n)
+ * 1. master_refresh_seed
+ * 2. kappa * n tranfer public keys, depths first: [0..n),...,[0..n)
+ * 3. hash over all pairs of R-values (for CS) if present, skipped otherwise
+ * 4. n denomination hashes, in order
+ * 5. amount with fee
+ * 6. kappa*n planchets, depths first: [0..n),...,[0..n)
*
* @since v27
* @deprecated vDOLDPLUS
* Hash over:
- * 1. refresh_seed (v27)
+ * 1. refresh_seed
* 2. hash over all pairs of R-values if present, skipped otherwise
* 3. n denomination hashes, in order
* 4. amount with fee
- * 5. kappa * n planchets, depths first: [0..n),[0..n),[0..n)
+ * 5. kappa * n planchets, depths first: [0..n),...,[0..n)
*/
struct GNUNET_HashCode session_hash;
};
diff --git a/core/api-exchange.rst b/core/api-exchange.rst
@@ -2026,7 +2026,7 @@ These endpoints are called by the client
The request body is a `RevealMeltRequest`.
- This endpoint was introduced in this form in protocol **v27**.
+ This endpoint was introduced in this form in protocol **vDOLDPLUS**.
:http:statuscode:`200 OK`:
The coin's' secret material matched the commitment and the original request was well-formed.
@@ -2062,12 +2062,17 @@ These endpoints are called by the client
// over Hash1a("Refresh", Cp, r, i), where Cp is the melted coin's public key,
// r is the public refresh nonce from the metling step and i runs over the
// _disclosed_ kappa-1 indices.
- signatures?: CoinSignature[kappa-1];
+ signatures: CoinSignature[kappa-1];
- // TODO[oec]: Add the transfer secrets
- transfer_secrets: string[kappa-1];
+ // @since vDOLDPLUS
+ // The seeds for the transfer secrets to reveal.
+ // For the kappa many batches of n coin candidates,
+ // each of the seeds in this list are expanded via HKDF:
+ // ``ts[k][] = HKDF(sizeof(HashCode)*n, ts_seeds[k], "ts")``
+ // An individual coin's transfer secret at kappa-index k and
+ // coin index i in the batch is then ``ts[k][i]``.
+ transfer_secret_seeds: HashCode[kappa-1];
- // TODO[oec]: Is this the right place?
// IFF the denomination of the old coin had support for age restriction,
// the client MUST provide the original age commitment, i. e. the
// vector of public keys, or omitted otherwise.
@@ -2110,7 +2115,7 @@ These endpoints are called by the client
The request body is a `RevealWithdrawRequest`.
- This endpoint was introduced in this form in protocol **v27**.
+ This endpoint was introduced in this form in protocol **vDOLDPLUS**.
:http:statuscode:`200 OK`:
The coin's' secret material matched the commitment and the original request was well-formed.
@@ -2668,30 +2673,57 @@ Coin History
// Melt fee.
melt_fee: Amount;
- // Commitment from the melt operation.
- rc: TALER_RefreshCommitmentP;
+ // Commitment from the melt operation, see `TALER_RefreshCommitmentP`
+ rc: HashCode;
- // Hash of the public denomination key used to sign the coin.
+ // Hash of the public denomination key used to sign the old coin.
// Needed because 'coin_sig' signs over this, and
// that is important to fix the coin's denomination.
- h_denom_pub: HashCode;
+ old_denom_pub_h: HashCode;
- // Seed from which the nonces for the n*κ coin candidates are derived
- // from.
- refresh_seed: HashCode;
+ // Hash over the age commitment of the coin. Optional.
+ old_age_commitment_h?: AgeCommitmentHash;
+
+ // @since vDOLDPLUS
+ // This value is opaque to the exchange. It was provided by the client
+ // as part of the original refresh request, and was therefore verified
+ // with the confirm_sig below.
+ // If the reveal step was not performed yet by the old coin owner,
+ // they can use this value and the old coin's private key to derive
+ // all indivual seeds for the n*κ coin candidates for the original
+ // refresh request and replay it
+ master_refresh_seed: HashCode;
+
+ // @since vDOLDPLUS
+ // The kappa*n list of transfer public keys that were provided by the
+ // old coin owner during the melt request.
+ transfer_pubs: EddsaPublicKey[kappa][];
+
+ // @since vDOLDPLUS
+ // The n denomination public keys for the fresh coins
+ // that the coin owner had requested.
+ denoms_h: HashCode[];
+
+ // @since vDOLDPLUS
+ // The `noreveal_index` value that was returned by the exchange as response
+ // to the melt request.
+ noreveal_index: Integer;
+
+ // @since vDOLDPLUS
+ // If the reveal step was successfully peformed by the coin owner,
+ // this field contains the blind coin signatures that were returned
+ // by the exchange for the chosen batch of coins.
+ ev_sigs?: BlindedDenominationSignature[];
// Master seed for the Clause-Schnorr R-value
// Present if one of the fresh coin's
// denominations is of type Clause-Schnorr.
blinding_seed?: BlindingMasterSeed;
- // Hash over the age commitment of the coin. Optional.
- h_age_commitment?: HashCode;
-
// Signature by the coin over a
// `TALER_RefreshMeltCoinAffirmationPS` of
// purpose ``TALER_SIGNATURE_WALLET_COIN_MELT``.
- coin_sig: EddsaSignature;
+ confirm_sig: EddsaSignature;
}
@@ -3419,9 +3451,27 @@ by anyone except the wallet itself.
// @since v27
// @deprecated vDOLDPLUS
- //
// Seed from which the nonces for the n*κ coin candidates are derived from.
- refresh_seed?: HashCode;
+ refresh_seed: HashCode;
+
+ // @since vDOLDPLUS
+ // This value is opaque to the exchange. It was provided by the client
+ // as part of the original refresh request, and was therefore
+ // verified with the coin_sig below.
+ //
+ // Note: The honest owner of the old coin SHOULD use this value
+ // and the old coin's private key to derive kappa many
+ // transfer secret seeds like this:
+ // ``ts_seeds[k] = SHA512(master_refresh_seed, old_coin_priv, "s", k)``
+ // Each of the kappa seeds is then expanded via HKDF:
+ // ``ts[k][] = HKDF(sizeof(HashCode)*n, ts_seeds[k], "ts")``
+ // An individual coin's transfer secret at kappa-index k and
+ // coin index i in the batch is then ``ts[k][i]``
+ // This ensures that the honest owner of the old coin can replay
+ // a MeltRequest from the coin history provided by the exchange
+ // (which includes this value), in case a wallet was restored
+ // from a backup into a state prior to the refresh operation.
+ master_refresh_seed: HashCode;
// Master seed for the Clause-Schnorr R-value
// creation. Must match the /blinding-prepare request.
@@ -3439,8 +3489,10 @@ by anyone except the wallet itself.
coin_evs: CoinEnvelope[kappa][];
// @since vDOLDPLUS
- // ``kappa`` arrays of ``n`` entries of transwer public keys each.
- // These are ephemeral ECDHE keys.
+ // ``kappa`` arrays of ``n`` entries of transfer public keys each.
+ // These are ephemeral ECDHE keys that allow the owner of a coin
+ // to (re-)obtain the derived coins from a refresh operation, f.e. should
+ // the wallet state be restored from a backup, prior to the refresh operation.
transfer_pubs: EddsaPublicKey[kappa][];
// Signature by the `coin <coin-priv>` over `TALER_RefreshMeltCoinAffirmationPS`.
@@ -3520,7 +3572,7 @@ became minted, and proof ownership of the coin itself.
Note that the original withdrawal fees will **not** be recouped.
- .. note:: This endpoint will become active with version **vRECOUP**, sometime after v27.
+ .. note:: This endpoint still Work-in-Progress. It will be implemented in **vRECOUP**, sometime after **vDOLDPLUS**.
**Request:**
@@ -3653,20 +3705,20 @@ became minted, and proof ownership of the coin itself.
}
-.. http:post:: /coins/$COIN_PUB/recoup-refresh
+.. http:post:: /recoup-refresh
Demand that a coin be refunded via wire transfer to the original owner.
- The base URL for ``/coins/``-requests may differ from the main base URL of the
+ The base URL for coin related requests may differ from the main base URL of the
exchange. The exchange MUST return a 307 or 308 redirection to the correct
base URL if this is the case.
The remaining amount on the coin will be credited to
- the old coin that ``$COIN_PUB`` was refreshed from.
+ the old coin that this coin was refreshed from.
Note that the original refresh fees will **not** be recouped.
- .. note:: This endpoint is not implemented and the API going to change after **v27**.
+ .. note:: This endpoint still Work-in-Progress. It will be implemented in **vRECOUP**, sometime after **vDOLDPLUS**.
**Request:**
@@ -3707,6 +3759,9 @@ became minted, and proof ownership of the coin itself.
.. ts:def:: RecoupRefreshRequest
interface RecoupRefreshRequest {
+ // The coin's public key
+ coin_pub: CoinPublicKey;
+
// Hash of denomination public key, specifying the type of coin the client
// would like the exchange to pay back.
denom_pub_hash: HashCode;