Contents

POST /melt#

“Melts” a coin. Invalidates the coins and prepares for exchanging of fresh coins. Taler uses a global parameter kappa for the cut-and-choose component of the protocol, for which this request is the commitment. Thus, various arguments are given kappa-times in this step. At present kappa is always 3.

The base URL for /melt/-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.

This endpoint was introduced in this form in protocol v32.

200 OK:

The request was successful. The response body is MeltResponse in this case.

403 Forbidden:

One of the signatures is invalid.

404 Not found:

The exchange does not recognize the denomination key as belonging to the exchange, or it has expired. If the denomination key is unknown, the response will be a DenominationUnknownMessage.

409 Conflict:

The operation is not allowed as the coin has insufficient residual value, or because the same public key of the coin has been previously used with a different denomination. Which case it is can be decided by looking at the error code (TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS or TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY). The response is MeltForbiddenResponse in both cases.

410 Gone:

The requested denomination key is not yet or no longer valid. It either before the validity start, past the expiration or was revoked. The response is a DenominationGoneMessage. Clients must evaluate the error code provided to understand which of the cases this is and handle it accordingly.

Details:

interface MeltRequest {
   // The old coin's public key
   old_coin_pub: CoinPublicKey;

   // Hash of the denomination public key of the old coin,
   // to determine total coin value.
   old_denom_pub_h: HashCode;

   // The hash of the age-commitment for the old coin. Only present
   // if the denomination has support for age restriction.
   old_age_commitment_h?: AgeCommitmentHash;

   // Signature over the old coin public key by the denomination.
   old_denom_sig: DenominationSignature;

   // Amount of the value of the old coin that should be melted as part of
   // this refresh operation, including melting fee.  I.e.:
   //        melting fee of the old coin
   //      + sum over all values of fresh coins
   //      + sum over all withdraw fees for the fresh coins
   value_with_fee: Amount;

   // @since v27
   // @deprecated **v32**
   // Seed from which the nonces for the n*κ coin candidates are derived from.
   //
   // @since **v32**
   // The refresh_seed is an opaque value to the exchange.
   // It is provided by the client and is verified with the coin_sig below.
   // Its purpose is to ensure that the honest owner of the old coin
   // can replay a /melt request from data in the coin history,
   // provided by the exchange and including this value, in case a wallet
   // was restored into a state prior to the refresh operation.
   //
   // The honest owner of the old coin SHOULD use this value
   // and the old coin's private key to derive kappa many
   // batch seeds (one for each cut-and-choose candidate)
   // like this:
   //
   //   bs[] = HKDF(kappa*sizeof(HashCode),
   //               "refresh-batch-seeds",
   //               old_coin_priv,
   //               refresh_seed)
   //
   // These batch seeds (however constructed) are relevant in the
   // subsequent reveal step of the cut-and-chose.  Each of the
   // revealed seeds is expanded to a batch of n transfer private keys
   // via HKDF:
   //
   //   tp[k][] = HKDF(n*sizeof(HashCode),
   //                  "refresh-transfer-private-keys",
   //                  bs[k])
   //
   // An individual coin's transfer private key at kappa-index k and
   // coin index i in the batch is then tp[k][i].  The corresponding
   // transfer _public_ keys are given in the field transfer_pubs.
   refresh_seed: HashCode;

   // Master seed for the Clause-Schnorr R-value
   // creation. Must match the /blinding-prepare request.
   // Must not have been used in any prior melt request.
   // Must be present if one of the fresh coin's
   // denominations is of type Clause-Schnorr.
   blinding_seed?: BlindingMasterSeed;

   // Array of n new hash codes of denomination public keys
   // for the new coins to order.
   denoms_h: HashCode[];

   // kappa arrays of n entries for blinded coin candidates,
   // each matching the respective entries in denoms_h.
   coin_evs: CoinEnvelope[kappa][];

   // @since **v32**
   // 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 over TALER_RefreshMeltCoinAffirmationPS.
   confirm_sig: EddsaSignature;

}

For details about the HKDF used to derive the new coin private keys and the blinding factors from ECDHE between the transfer public keys and the private key of the melted coin, please refer to the implementation in libtalerutil.

interface MeltResponse {
  // Which of the kappa indices does the client not have to reveal
  // by calling the /reveal-melt endpoint.
  noreveal_index: Integer;

  // Signature of TALER_RefreshMeltConfirmationPS whereby the exchange
  // affirms the successful melt and confirming the noreveal_index.
  exchange_sig: EddsaSignature;

  // Public EdDSA key of the exchange that was used to generate the signature.
  // Should match one of the exchange's signing keys from /keys.  Again given
  // explicitly as the client might otherwise be confused by clock skew as to
  // which signing key was used.
  exchange_pub: EddsaPublicKey;

  // Base URL to use for operations on the refresh context
  // (so the reveal operation).  If not given,
  // the base URL is the same as the one used for this request.
  // Can be used if the base URL for /reveal-melt/ differs from that
  // for /melt/, i.e. for load balancing.  Clients SHOULD
  // respect the reveal_base_url if provided.  Any HTTP server
  // belonging to an exchange MUST generate a 307 or 308 redirection
  // to the correct base URL should a client uses the wrong base
  // URL, or if the base URL has changed since the melt.
  //
  // When melting the same coin twice (technically allowed
  // as the response might have been lost on the network),
  // the exchange may return different values for the reveal_base_url.
  reveal_base_url?: string;

}
interface MeltForbiddenResponse {

  // Must be TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS
  // or TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY
  code: Integer;

  // A string explaining that the user tried to
  // double-spend.
  hint: string;

  // EdDSA public key of a coin being double-spent.
  coin_pub: EddsaPublicKey;

  // Hash of the public key of the denomination of the coin.
  h_denom_pub: HashCode;

}