gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: towards implementing breaking


From: gnunet
Subject: [taler-wallet-core] branch master updated: towards implementing breaking exchange protocol changes
Date: Mon, 21 Feb 2022 12:41:00 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 5c93f151 towards implementing breaking exchange protocol changes
5c93f151 is described below

commit 5c93f15157b4fc9d0fefb6bb2a9956592ebb1ec9
Author: Florian Dold <florian@dold.me>
AuthorDate: Mon Feb 21 12:40:51 2022 +0100

    towards implementing breaking exchange protocol changes
---
 packages/taler-util/src/talerCrypto.ts             | 115 ++++++--
 packages/taler-util/src/talerTypes.ts              | 174 +++++-------
 packages/taler-util/src/walletTypes.ts             | 313 +++++++++++----------
 packages/taler-wallet-cli/src/harness/harness.ts   |   2 +-
 .../integrationtests/test-exchange-timetravel.ts   |   4 +-
 .../taler-wallet-core/src/crypto/cryptoTypes.ts    |  29 +-
 .../src/crypto/workers/cryptoApi.ts                |   3 +-
 .../src/crypto/workers/cryptoImplementation.ts     | 237 +++++++---------
 packages/taler-wallet-core/src/db.ts               |  47 ++--
 .../src/operations/backup/import.ts                |   3 +-
 .../src/operations/backup/index.ts                 |  62 ++--
 .../taler-wallet-core/src/operations/deposits.ts   |  66 ++---
 .../taler-wallet-core/src/operations/exchanges.ts  |  10 +-
 packages/taler-wallet-core/src/operations/pay.ts   |  10 +-
 .../taler-wallet-core/src/operations/refresh.ts    |  43 +--
 packages/taler-wallet-core/src/operations/tip.ts   |  44 +--
 .../src/operations/withdraw.test.ts                |  14 +-
 .../taler-wallet-core/src/operations/withdraw.ts   |  29 +-
 .../src/util/coinSelection.test.ts                 |  14 +-
 19 files changed, 560 insertions(+), 659 deletions(-)

diff --git a/packages/taler-util/src/talerCrypto.ts 
b/packages/taler-util/src/talerCrypto.ts
index c2640317..40d2e55d 100644
--- a/packages/taler-util/src/talerCrypto.ts
+++ b/packages/taler-util/src/talerCrypto.ts
@@ -22,10 +22,15 @@
  * Imports.
  */
 import * as nacl from "./nacl-fast.js";
-import { kdf } from "./kdf.js";
+import { kdf, kdfKw } from "./kdf.js";
 import bigint from "big-integer";
-import { DenominationPubKey, DenomKeyType } from "./talerTypes.js";
-import { AssertionError, equal } from "assert";
+import {
+  CoinEnvelope,
+  DenominationPubKey,
+  DenomKeyType,
+  HashCodeString,
+} from "./talerTypes.js";
+import { Logger } from "./logging.js";
 
 export function getRandomBytes(n: number): Uint8Array {
   return nacl.randomBytes(n);
@@ -365,7 +370,7 @@ export type CsBlindingSecrets = {
   beta: [Uint8Array, Uint8Array];
 };
 
-function typedArrayConcat(chunks: Uint8Array[]): Uint8Array {
+export function typedArrayConcat(chunks: Uint8Array[]): Uint8Array {
   let payloadLen = 0;
   for (const c of chunks) {
     payloadLen += c.byteLength;
@@ -490,9 +495,7 @@ export function deriveBSeed(
  * @param coinPriv coin private key
  * @returns nonce
  */
-export function deriveWithdrawNonce(
-  coinPriv: Uint8Array,
-): Uint8Array { 
+export function deriveWithdrawNonce(coinPriv: Uint8Array): Uint8Array {
   const outLen = 32;
   const salt = stringToBytes("n");
   return kdf(outLen, coinPriv, salt);
@@ -539,7 +542,7 @@ export async function csUnblind(
   csSig: CsBlindSignature,
 ): Promise<CsSignature> {
   if (b != 0 && b != 1) {
-    throw new AssertionError();
+    throw new Error();
   }
   const secrets = deriveSecrets(bseed);
   const rPubDash = (await calcRBlind(csPub, secrets, rPub))[b];
@@ -595,9 +598,38 @@ export function hash(d: Uint8Array): Uint8Array {
   return nacl.hash(d);
 }
 
+export function hashCoinEv(
+  coinEv: CoinEnvelope,
+  denomPubHash: HashCodeString,
+): Uint8Array {
+  const hashContext = createHashContext();
+  hashContext.update(decodeCrock(denomPubHash));
+  hashCoinEvInner(coinEv, hashContext);
+  return hashContext.finish();
+}
+
+const logger = new Logger("talerCrypto.ts");
+
+export function hashCoinEvInner(
+  coinEv: CoinEnvelope,
+  hashState: nacl.HashState,
+): void {
+  const hashInputBuf = new ArrayBuffer(4);
+  const uint8ArrayBuf = new Uint8Array(hashInputBuf);
+  const dv = new DataView(hashInputBuf);
+  dv.setUint32(0, DenomKeyType.toIntTag(coinEv.cipher));
+  hashState.update(uint8ArrayBuf);
+  switch (coinEv.cipher) {
+    case DenomKeyType.Rsa:
+      hashState.update(decodeCrock(coinEv.rsa_blinded_planchet));
+      return;
+    default:
+      throw new Error();
+  }
+}
+
 /**
- * Hash a denomination public key according to the
- * algorithm of exchange protocol v10.
+ * Hash a denomination public key.
  */
 export function hashDenomPub(pub: DenominationPubKey): Uint8Array {
   if (pub.cipher === DenomKeyType.Rsa) {
@@ -606,18 +638,16 @@ export function hashDenomPub(pub: DenominationPubKey): 
Uint8Array {
     const uint8ArrayBuf = new Uint8Array(hashInputBuf);
     const dv = new DataView(hashInputBuf);
     dv.setUint32(0, pub.age_mask ?? 0);
-    dv.setUint32(4, pub.cipher);
+    dv.setUint32(4, DenomKeyType.toIntTag(pub.cipher));
     uint8ArrayBuf.set(pubBuf, 8);
     return nacl.hash(uint8ArrayBuf);
-  } else if (pub.cipher === DenomKeyType.LegacyRsa) {
-    return hash(decodeCrock(pub.rsa_public_key));
   } else if (pub.cipher === DenomKeyType.ClauseSchnorr) {
     const pubBuf = decodeCrock(pub.cs_public_key);
     const hashInputBuf = new ArrayBuffer(pubBuf.length + 4 + 4);
     const uint8ArrayBuf = new Uint8Array(hashInputBuf);
     const dv = new DataView(hashInputBuf);
     dv.setUint32(0, pub.age_mask ?? 0);
-    dv.setUint32(4, pub.cipher);
+    dv.setUint32(4, DenomKeyType.toIntTag(pub.cipher));
     uint8ArrayBuf.set(pubBuf, 8);
     return nacl.hash(uint8ArrayBuf);
   } else {
@@ -652,18 +682,57 @@ export interface FreshCoin {
   bks: Uint8Array;
 }
 
+// export function setupRefreshPlanchet(
+//   secretSeed: Uint8Array,
+//   coinNumber: number,
+// ): FreshCoin {
+//   const info = stringToBytes("taler-coin-derivation");
+//   const saltArrBuf = new ArrayBuffer(4);
+//   const salt = new Uint8Array(saltArrBuf);
+//   const saltDataView = new DataView(saltArrBuf);
+//   saltDataView.setUint32(0, coinNumber);
+//   const out = kdf(64, secretSeed, salt, info);
+//   const coinPriv = out.slice(0, 32);
+//   const bks = out.slice(32, 64);
+//   return {
+//     bks,
+//     coinPriv,
+//     coinPub: eddsaGetPublic(coinPriv),
+//   };
+// }
+
+function bufferForUint32(n: number): Uint8Array {
+  const arrBuf = new ArrayBuffer(4);
+  const buf = new Uint8Array(arrBuf);
+  const dv = new DataView(arrBuf);
+  dv.setUint32(0, n);
+  return buf;
+}
+
 export function setupRefreshPlanchet(
-  secretSeed: Uint8Array,
+  transferSecret: Uint8Array,
   coinNumber: number,
 ): FreshCoin {
-  const info = stringToBytes("taler-coin-derivation");
-  const saltArrBuf = new ArrayBuffer(4);
-  const salt = new Uint8Array(saltArrBuf);
-  const saltDataView = new DataView(saltArrBuf);
-  saltDataView.setUint32(0, coinNumber);
-  const out = kdf(64, secretSeed, salt, info);
-  const coinPriv = out.slice(0, 32);
-  const bks = out.slice(32, 64);
+  // See TALER_transfer_secret_to_planchet_secret in C impl
+  const planchetMasterSecret = kdfKw({
+    ikm: transferSecret,
+    outputLength: 32,
+    salt: bufferForUint32(coinNumber),
+    info: stringToBytes("taler-coin-derivation"),
+  });
+
+  const coinPriv = kdfKw({
+    ikm: planchetMasterSecret,
+    outputLength: 32,
+    salt: stringToBytes("coin"),
+  });
+
+  const bks = kdfKw({
+    ikm: planchetMasterSecret,
+    outputLength: 32,
+    salt: stringToBytes("bks"),
+  });
+
   return {
     bks,
     coinPriv,
diff --git a/packages/taler-util/src/talerTypes.ts 
b/packages/taler-util/src/talerTypes.ts
index 7305122b..59d37dec 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -60,11 +60,8 @@ export class ExchangeDenomination {
 
   /**
    * Public signing key of the denomination.
-   *
-   * The "string" alternative is for the old exchange protocol (v9) that
-   * only supports RSA keys.
    */
-  denom_pub: DenominationPubKey | string;
+  denom_pub: DenominationPubKey;
 
   /**
    * Fee for withdrawing.
@@ -162,7 +159,7 @@ export interface RecoupRequest {
    *
    * The string variant is for the legacy exchange protocol.
    */
-  denom_sig: UnblindedSignature | string;
+  denom_sig: UnblindedSignature;
 
   /**
    * Blinding key that was used during withdraw,
@@ -188,7 +185,7 @@ export interface RecoupRefreshRequest {
    *
    * The string variant is for the legacy exchange protocol.
    */
-  denom_sig: UnblindedSignature | string;
+  denom_sig: UnblindedSignature;
 
   /**
    * Coin's blinding factor.
@@ -218,20 +215,13 @@ export interface RecoupConfirmation {
   old_coin_pub?: string;
 }
 
-export type UnblindedSignature =
-  | RsaUnblindedSignature
-  | LegacyRsaUnblindedSignature;
+export type UnblindedSignature = RsaUnblindedSignature;
 
 export interface RsaUnblindedSignature {
   cipher: DenomKeyType.Rsa;
   rsa_signature: string;
 }
 
-export interface LegacyRsaUnblindedSignature {
-  cipher: DenomKeyType.LegacyRsa;
-  rsa_signature: string;
-}
-
 /**
  * Deposit permission for a single coin.
  */
@@ -252,7 +242,7 @@ export interface CoinDepositPermission {
    * The string variant is for legacy protocol support.
    */
 
-  ub_sig: UnblindedSignature | string;
+  ub_sig: UnblindedSignature;
 
   /**
    * The denomination public key associated with this coin.
@@ -841,18 +831,23 @@ export class TipPickupGetResponse {
 }
 
 export enum DenomKeyType {
-  Rsa = 1,
-  ClauseSchnorr = 2,
-  LegacyRsa = 3,
+  Rsa = "RSA",
+  ClauseSchnorr = "CS",
 }
 
-export interface RsaBlindedDenominationSignature {
-  cipher: DenomKeyType.Rsa;
-  blinded_rsa_signature: string;
+export namespace DenomKeyType {
+  export function toIntTag(t: DenomKeyType): number {
+    switch (t) {
+      case DenomKeyType.Rsa:
+        return 1;
+      case DenomKeyType.ClauseSchnorr:
+        return 2;
+    }
+  }
 }
 
-export interface LegacyRsaBlindedDenominationSignature {
-  cipher: DenomKeyType.LegacyRsa;
+export interface RsaBlindedDenominationSignature {
+  cipher: DenomKeyType.Rsa;
   blinded_rsa_signature: string;
 }
 
@@ -862,33 +857,22 @@ export interface CSBlindedDenominationSignature {
 
 export type BlindedDenominationSignature =
   | RsaBlindedDenominationSignature
-  | CSBlindedDenominationSignature
-  | LegacyRsaBlindedDenominationSignature;
+  | CSBlindedDenominationSignature;
 
 export const codecForBlindedDenominationSignature = () =>
   buildCodecForUnion<BlindedDenominationSignature>()
     .discriminateOn("cipher")
-    .alternative(1, codecForRsaBlindedDenominationSignature())
-    .alternative(3, codecForLegacyRsaBlindedDenominationSignature())
+    .alternative(DenomKeyType.Rsa, codecForRsaBlindedDenominationSignature())
     .build("BlindedDenominationSignature");
 
 export const codecForRsaBlindedDenominationSignature = () =>
   buildCodecForObject<RsaBlindedDenominationSignature>()
-    .property("cipher", codecForConstNumber(1))
+    .property("cipher", codecForConstString(DenomKeyType.Rsa))
     .property("blinded_rsa_signature", codecForString())
     .build("RsaBlindedDenominationSignature");
 
-export const codecForLegacyRsaBlindedDenominationSignature = () =>
-  buildCodecForObject<LegacyRsaBlindedDenominationSignature>()
-    .property("cipher", codecForConstNumber(1))
-    .property("blinded_rsa_signature", codecForString())
-    .build("LegacyRsaBlindedDenominationSignature");
-
 export class WithdrawResponse {
-  /**
-   * The string variant is for legacy protocol support.
-   */
-  ev_sig: BlindedDenominationSignature | string;
+  ev_sig: BlindedDenominationSignature;
 }
 
 /**
@@ -983,10 +967,7 @@ export interface ExchangeMeltResponse {
 }
 
 export interface ExchangeRevealItem {
-  /**
-   * The string variant is for the legacy v9 protocol.
-   */
-  ev_sig: BlindedDenominationSignature | string;
+  ev_sig: BlindedDenominationSignature;
 }
 
 export interface ExchangeRevealResponse {
@@ -1105,26 +1086,18 @@ export interface BankWithdrawalOperationPostResponse {
   transfer_done: boolean;
 }
 
-export type DenominationPubKey =
-  | RsaDenominationPubKey
-  | CsDenominationPubKey
-  | LegacyRsaDenominationPubKey;
-
-export interface LegacyRsaDenominationPubKey {
-  cipher: DenomKeyType.LegacyRsa;
-  rsa_public_key: string;
-}
+export type DenominationPubKey = RsaDenominationPubKey | CsDenominationPubKey;
 
 export interface RsaDenominationPubKey {
-  cipher: DenomKeyType.Rsa;
-  rsa_public_key: string;
-  age_mask?: number;
+  readonly cipher: DenomKeyType.Rsa;
+  readonly rsa_public_key: string;
+  readonly age_mask?: number;
 }
 
 export interface CsDenominationPubKey {
-  cipher: DenomKeyType.ClauseSchnorr;
-  age_mask: number;
-  cs_public_key: string;
+  readonly cipher: DenomKeyType.ClauseSchnorr;
+  readonly age_mask: number;
+  readonly cs_public_key: string;
 }
 
 export namespace DenominationPubKey {
@@ -1136,12 +1109,6 @@ export namespace DenominationPubKey {
       return -1;
     } else if (p1.cipher > p2.cipher) {
       return +1;
-    }
-    if (
-      p1.cipher === DenomKeyType.LegacyRsa &&
-      p2.cipher === DenomKeyType.LegacyRsa
-    ) {
-      return strcmp(p1.rsa_public_key, p2.rsa_public_key);
     } else if (
       p1.cipher === DenomKeyType.Rsa &&
       p2.cipher === DenomKeyType.Rsa
@@ -1166,41 +1133,24 @@ export namespace DenominationPubKey {
       throw Error("unsupported cipher");
     }
   }
-
-  export function lift(p1: DenominationPubKey | string): DenominationPubKey {
-    if (typeof p1 === "string") {
-      return {
-        cipher: DenomKeyType.LegacyRsa,
-        rsa_public_key: p1,
-      };
-    }
-    return p1;
-  }
 }
 
 export const codecForDenominationPubKey = () =>
   buildCodecForUnion<DenominationPubKey>()
     .discriminateOn("cipher")
-    .alternative(1, codecForRsaDenominationPubKey())
-    .alternative(2, codecForCsDenominationPubKey())
-    .alternative(3, codecForLegacyRsaDenominationPubKey())
+    .alternative(DenomKeyType.Rsa, codecForRsaDenominationPubKey())
+    .alternative(DenomKeyType.ClauseSchnorr, codecForCsDenominationPubKey())
     .build("DenominationPubKey");
 
 export const codecForRsaDenominationPubKey = () =>
   buildCodecForObject<RsaDenominationPubKey>()
-    .property("cipher", codecForConstNumber(1))
+    .property("cipher", codecForConstString(DenomKeyType.Rsa))
     .property("rsa_public_key", codecForString())
     .build("DenominationPubKey");
 
-export const codecForLegacyRsaDenominationPubKey = () =>
-  buildCodecForObject<LegacyRsaDenominationPubKey>()
-    .property("cipher", codecForConstNumber(3))
-    .property("rsa_public_key", codecForString())
-    .build("LegacyRsaDenominationPubKey");
-
 export const codecForCsDenominationPubKey = () =>
   buildCodecForObject<CsDenominationPubKey>()
-    .property("cipher", codecForConstNumber(2))
+    .property("cipher", codecForConstString(DenomKeyType.ClauseSchnorr))
     .property("cs_public_key", codecForString())
     .build("CsDenominationPubKey");
 
@@ -1219,10 +1169,7 @@ export type CoinPublicKeyString = string;
 export const codecForDenomination = (): Codec<ExchangeDenomination> =>
   buildCodecForObject<ExchangeDenomination>()
     .property("value", codecForString())
-    .property(
-      "denom_pub",
-      codecForEither(codecForDenominationPubKey(), codecForString()),
-    )
+    .property("denom_pub", codecForDenominationPubKey())
     .property("fee_withdraw", codecForString())
     .property("fee_deposit", codecForString())
     .property("fee_refresh", codecForString())
@@ -1470,10 +1417,7 @@ export const codecForRecoupConfirmation = (): 
Codec<RecoupConfirmation> =>
 
 export const codecForWithdrawResponse = (): Codec<WithdrawResponse> =>
   buildCodecForObject<WithdrawResponse>()
-    .property(
-      "ev_sig",
-      codecForEither(codecForBlindedDenominationSignature(), codecForString()),
-    )
+    .property("ev_sig", codecForBlindedDenominationSignature())
     .build("WithdrawResponse");
 
 export const codecForMerchantPayResponse = (): Codec<MerchantPayResponse> =>
@@ -1491,10 +1435,7 @@ export const codecForExchangeMeltResponse = (): 
Codec<ExchangeMeltResponse> =>
 
 export const codecForExchangeRevealItem = (): Codec<ExchangeRevealItem> =>
   buildCodecForObject<ExchangeRevealItem>()
-    .property(
-      "ev_sig",
-      codecForEither(codecForBlindedDenominationSignature(), codecForString()),
-    )
+    .property("ev_sig", codecForBlindedDenominationSignature())
     .build("ExchangeRevealItem");
 
 export const codecForExchangeRevealResponse =
@@ -1711,17 +1652,48 @@ export const codecForMerchantConfigResponse =
       .build("MerchantConfigResponse");
 
 export enum ExchangeProtocolVersion {
-  V9 = 9,
+  /**
+   * Current version supported by the wallet.
+   */
   V12 = 12,
 }
 
 export enum MerchantProtocolVersion {
-  /**
-   * Legacy version that is still supported.
-   */
-  V1 = 1,
   /**
    * Current version supported by the wallet.
    */
   V3 = 3,
 }
+
+export type CoinEnvelope = CoinEnvelopeRsa | CoinEnvelopeCs;
+
+export interface CoinEnvelopeRsa {
+  cipher: DenomKeyType.Rsa;
+  rsa_blinded_planchet: string;
+}
+
+export interface CoinEnvelopeCs {
+  cipher: DenomKeyType.ClauseSchnorr;
+  // FIXME: add remaining fields
+}
+
+export type HashCodeString = string;
+
+export interface ExchangeWithdrawRequest {
+  denom_pub_hash: HashCodeString;
+  reserve_sig: EddsaSignatureString;
+  coin_ev: CoinEnvelope;
+}
+
+export interface ExchangeRefreshRevealRequest {
+  new_denoms_h: HashCodeString[];
+  coin_evs: CoinEnvelope[];
+  /**
+   * kappa - 1 transfer private keys (ephemeral ECDHE keys).
+   */
+  transfer_privs: string[];
+
+  transfer_pub: EddsaPublicKeyString;
+
+  link_sigs: EddsaSignatureString[];
+}
diff --git a/packages/taler-util/src/walletTypes.ts 
b/packages/taler-util/src/walletTypes.ts
index 4a871e74..2219316b 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -47,6 +47,7 @@ import {
 import {
   AmountString,
   codecForContractTerms,
+  CoinEnvelope,
   ContractTerms,
   DenominationPubKey,
   DenomKeyType,
@@ -136,11 +137,12 @@ export interface ConfirmPayResultPending {
 
 export type ConfirmPayResult = ConfirmPayResultDone | ConfirmPayResultPending;
 
-export const codecForConfirmPayResultPending = (): 
Codec<ConfirmPayResultPending> =>
-  buildCodecForObject<ConfirmPayResultPending>()
-    .property("lastError", codecForAny())
-    .property("type", codecForConstString(ConfirmPayResultType.Pending))
-    .build("ConfirmPayResultPending");
+export const codecForConfirmPayResultPending =
+  (): Codec<ConfirmPayResultPending> =>
+    buildCodecForObject<ConfirmPayResultPending>()
+      .property("lastError", codecForAny())
+      .property("type", codecForConstString(ConfirmPayResultType.Pending))
+      .build("ConfirmPayResultPending");
 
 export const codecForConfirmPayResultDone = (): Codec<ConfirmPayResultDone> =>
   buildCodecForObject<ConfirmPayResultDone>()
@@ -322,45 +324,48 @@ export enum PreparePayResultType {
   AlreadyConfirmed = "already-confirmed",
 }
 
-export const codecForPreparePayResultPaymentPossible = (): 
Codec<PreparePayResultPaymentPossible> =>
-  buildCodecForObject<PreparePayResultPaymentPossible>()
-    .property("amountEffective", codecForAmountString())
-    .property("amountRaw", codecForAmountString())
-    .property("contractTerms", codecForContractTerms())
-    .property("proposalId", codecForString())
-    .property("contractTermsHash", codecForString())
-    .property("noncePriv", codecForString())
-    .property(
-      "status",
-      codecForConstString(PreparePayResultType.PaymentPossible),
-    )
-    .build("PreparePayResultPaymentPossible");
-
-export const codecForPreparePayResultInsufficientBalance = (): 
Codec<PreparePayResultInsufficientBalance> =>
-  buildCodecForObject<PreparePayResultInsufficientBalance>()
-    .property("amountRaw", codecForAmountString())
-    .property("contractTerms", codecForAny())
-    .property("proposalId", codecForString())
-    .property("noncePriv", codecForString())
-    .property(
-      "status",
-      codecForConstString(PreparePayResultType.InsufficientBalance),
-    )
-    .build("PreparePayResultInsufficientBalance");
-
-export const codecForPreparePayResultAlreadyConfirmed = (): 
Codec<PreparePayResultAlreadyConfirmed> =>
-  buildCodecForObject<PreparePayResultAlreadyConfirmed>()
-    .property(
-      "status",
-      codecForConstString(PreparePayResultType.AlreadyConfirmed),
-    )
-    .property("amountEffective", codecForAmountString())
-    .property("amountRaw", codecForAmountString())
-    .property("paid", codecForBoolean())
-    .property("contractTerms", codecForAny())
-    .property("contractTermsHash", codecForString())
-    .property("proposalId", codecForString())
-    .build("PreparePayResultAlreadyConfirmed");
+export const codecForPreparePayResultPaymentPossible =
+  (): Codec<PreparePayResultPaymentPossible> =>
+    buildCodecForObject<PreparePayResultPaymentPossible>()
+      .property("amountEffective", codecForAmountString())
+      .property("amountRaw", codecForAmountString())
+      .property("contractTerms", codecForContractTerms())
+      .property("proposalId", codecForString())
+      .property("contractTermsHash", codecForString())
+      .property("noncePriv", codecForString())
+      .property(
+        "status",
+        codecForConstString(PreparePayResultType.PaymentPossible),
+      )
+      .build("PreparePayResultPaymentPossible");
+
+export const codecForPreparePayResultInsufficientBalance =
+  (): Codec<PreparePayResultInsufficientBalance> =>
+    buildCodecForObject<PreparePayResultInsufficientBalance>()
+      .property("amountRaw", codecForAmountString())
+      .property("contractTerms", codecForAny())
+      .property("proposalId", codecForString())
+      .property("noncePriv", codecForString())
+      .property(
+        "status",
+        codecForConstString(PreparePayResultType.InsufficientBalance),
+      )
+      .build("PreparePayResultInsufficientBalance");
+
+export const codecForPreparePayResultAlreadyConfirmed =
+  (): Codec<PreparePayResultAlreadyConfirmed> =>
+    buildCodecForObject<PreparePayResultAlreadyConfirmed>()
+      .property(
+        "status",
+        codecForConstString(PreparePayResultType.AlreadyConfirmed),
+      )
+      .property("amountEffective", codecForAmountString())
+      .property("amountRaw", codecForAmountString())
+      .property("paid", codecForBoolean())
+      .property("contractTerms", codecForAny())
+      .property("contractTermsHash", codecForString())
+      .property("proposalId", codecForString())
+      .build("PreparePayResultAlreadyConfirmed");
 
 export const codecForPreparePayResult = (): Codec<PreparePayResult> =>
   buildCodecForUnion<PreparePayResult>()
@@ -461,7 +466,7 @@ export interface PlanchetCreationResult {
   denomPub: DenominationPubKey;
   blindingKey: string;
   withdrawSig: string;
-  coinEv: string;
+  coinEv: CoinEnvelope;
   coinValue: AmountJson;
   coinEvHash: string;
 }
@@ -543,12 +548,13 @@ export interface ExchangeListItem {
   tos: ExchangeTos;
 }
 
-const codecForExchangeTos = (): Codec<ExchangeTos> => 
buildCodecForObject<ExchangeTos>()
-  .property("acceptedVersion", codecOptional(codecForString()))
-  .property("currentVersion", codecOptional(codecForString()))
-  .property("contentType", codecOptional(codecForString()))
-  .property("content", codecOptional(codecForString()))
-  .build("ExchangeTos")
+const codecForExchangeTos = (): Codec<ExchangeTos> =>
+  buildCodecForObject<ExchangeTos>()
+    .property("acceptedVersion", codecOptional(codecForString()))
+    .property("currentVersion", codecOptional(codecForString()))
+    .property("contentType", codecOptional(codecForString()))
+    .property("content", codecOptional(codecForString()))
+    .build("ExchangeTos");
 
 export const codecForExchangeListItem = (): Codec<ExchangeListItem> =>
   buildCodecForObject<ExchangeListItem>()
@@ -670,10 +676,11 @@ export interface ForceExchangeUpdateRequest {
   exchangeBaseUrl: string;
 }
 
-export const codecForForceExchangeUpdateRequest = (): 
Codec<AddExchangeRequest> =>
-  buildCodecForObject<AddExchangeRequest>()
-    .property("exchangeBaseUrl", codecForString())
-    .build("AddExchangeRequest");
+export const codecForForceExchangeUpdateRequest =
+  (): Codec<AddExchangeRequest> =>
+    buildCodecForObject<AddExchangeRequest>()
+      .property("exchangeBaseUrl", codecForString())
+      .build("AddExchangeRequest");
 
 export interface GetExchangeTosRequest {
   exchangeBaseUrl: string;
@@ -691,11 +698,12 @@ export interface AcceptManualWithdrawalRequest {
   amount: string;
 }
 
-export const codecForAcceptManualWithdrawalRequet = (): 
Codec<AcceptManualWithdrawalRequest> =>
-  buildCodecForObject<AcceptManualWithdrawalRequest>()
-    .property("exchangeBaseUrl", codecForString())
-    .property("amount", codecForString())
-    .build("AcceptManualWithdrawalRequest");
+export const codecForAcceptManualWithdrawalRequet =
+  (): Codec<AcceptManualWithdrawalRequest> =>
+    buildCodecForObject<AcceptManualWithdrawalRequest>()
+      .property("exchangeBaseUrl", codecForString())
+      .property("amount", codecForString())
+      .build("AcceptManualWithdrawalRequest");
 
 export interface GetWithdrawalDetailsForAmountRequest {
   exchangeBaseUrl: string;
@@ -707,28 +715,31 @@ export interface AcceptBankIntegratedWithdrawalRequest {
   exchangeBaseUrl: string;
 }
 
-export const codecForAcceptBankIntegratedWithdrawalRequest = (): 
Codec<AcceptBankIntegratedWithdrawalRequest> =>
-  buildCodecForObject<AcceptBankIntegratedWithdrawalRequest>()
-    .property("exchangeBaseUrl", codecForString())
-    .property("talerWithdrawUri", codecForString())
-    .build("AcceptBankIntegratedWithdrawalRequest");
+export const codecForAcceptBankIntegratedWithdrawalRequest =
+  (): Codec<AcceptBankIntegratedWithdrawalRequest> =>
+    buildCodecForObject<AcceptBankIntegratedWithdrawalRequest>()
+      .property("exchangeBaseUrl", codecForString())
+      .property("talerWithdrawUri", codecForString())
+      .build("AcceptBankIntegratedWithdrawalRequest");
 
-export const codecForGetWithdrawalDetailsForAmountRequest = (): 
Codec<GetWithdrawalDetailsForAmountRequest> =>
-  buildCodecForObject<GetWithdrawalDetailsForAmountRequest>()
-    .property("exchangeBaseUrl", codecForString())
-    .property("amount", codecForString())
-    .build("GetWithdrawalDetailsForAmountRequest");
+export const codecForGetWithdrawalDetailsForAmountRequest =
+  (): Codec<GetWithdrawalDetailsForAmountRequest> =>
+    buildCodecForObject<GetWithdrawalDetailsForAmountRequest>()
+      .property("exchangeBaseUrl", codecForString())
+      .property("amount", codecForString())
+      .build("GetWithdrawalDetailsForAmountRequest");
 
 export interface AcceptExchangeTosRequest {
   exchangeBaseUrl: string;
   etag: string;
 }
 
-export const codecForAcceptExchangeTosRequest = (): 
Codec<AcceptExchangeTosRequest> =>
-  buildCodecForObject<AcceptExchangeTosRequest>()
-    .property("exchangeBaseUrl", codecForString())
-    .property("etag", codecForString())
-    .build("AcceptExchangeTosRequest");
+export const codecForAcceptExchangeTosRequest =
+  (): Codec<AcceptExchangeTosRequest> =>
+    buildCodecForObject<AcceptExchangeTosRequest>()
+      .property("exchangeBaseUrl", codecForString())
+      .property("etag", codecForString())
+      .build("AcceptExchangeTosRequest");
 
 export interface ApplyRefundRequest {
   talerRefundUri: string;
@@ -742,18 +753,20 @@ export const codecForApplyRefundRequest = (): 
Codec<ApplyRefundRequest> =>
 export interface GetWithdrawalDetailsForUriRequest {
   talerWithdrawUri: string;
 }
-export const codecForGetWithdrawalDetailsForUri = (): 
Codec<GetWithdrawalDetailsForUriRequest> =>
-  buildCodecForObject<GetWithdrawalDetailsForUriRequest>()
-    .property("talerWithdrawUri", codecForString())
-    .build("GetWithdrawalDetailsForUriRequest");
+export const codecForGetWithdrawalDetailsForUri =
+  (): Codec<GetWithdrawalDetailsForUriRequest> =>
+    buildCodecForObject<GetWithdrawalDetailsForUriRequest>()
+      .property("talerWithdrawUri", codecForString())
+      .build("GetWithdrawalDetailsForUriRequest");
 
 export interface ListKnownBankAccountsRequest {
   currency?: string;
 }
-export const codecForListKnownBankAccounts = (): 
Codec<ListKnownBankAccountsRequest> =>
-  buildCodecForObject<ListKnownBankAccountsRequest>()
-    .property("currency", codecOptional(codecForString()))
-    .build("ListKnownBankAccountsRequest");
+export const codecForListKnownBankAccounts =
+  (): Codec<ListKnownBankAccountsRequest> =>
+    buildCodecForObject<ListKnownBankAccountsRequest>()
+      .property("currency", codecOptional(codecForString()))
+      .build("ListKnownBankAccountsRequest");
 
 export interface GetExchangeWithdrawalInfo {
   exchangeBaseUrl: string;
@@ -761,15 +774,16 @@ export interface GetExchangeWithdrawalInfo {
   tosAcceptedFormat?: string[];
 }
 
-export const codecForGetExchangeWithdrawalInfo = (): 
Codec<GetExchangeWithdrawalInfo> =>
-  buildCodecForObject<GetExchangeWithdrawalInfo>()
-    .property("exchangeBaseUrl", codecForString())
-    .property("amount", codecForAmountJson())
-    .property(
-      "tosAcceptedFormat",
-      codecOptional(codecForList(codecForString())),
-    )
-    .build("GetExchangeWithdrawalInfo");
+export const codecForGetExchangeWithdrawalInfo =
+  (): Codec<GetExchangeWithdrawalInfo> =>
+    buildCodecForObject<GetExchangeWithdrawalInfo>()
+      .property("exchangeBaseUrl", codecForString())
+      .property("amount", codecForAmountJson())
+      .property(
+        "tosAcceptedFormat",
+        codecOptional(codecForList(codecForString())),
+      )
+      .build("GetExchangeWithdrawalInfo");
 
 export interface AbortProposalRequest {
   proposalId: string;
@@ -853,17 +867,17 @@ export interface RefreshPlanchetInfo {
   /**
    * Public key for the coin.
    */
-  publicKey: string;
+  coinPub: string;
 
   /**
    * Private key for the coin.
    */
-  privateKey: string;
+  coinPriv: string;
 
   /**
    * Blinded public key.
    */
-  coinEv: string;
+  coinEv: CoinEnvelope;
 
   coinEvHash: string;
 
@@ -896,12 +910,13 @@ export interface RecoveryLoadRequest {
   strategy?: RecoveryMergeStrategy;
 }
 
-export const codecForWithdrawTestBalance = (): 
Codec<WithdrawTestBalanceRequest> =>
-  buildCodecForObject<WithdrawTestBalanceRequest>()
-    .property("amount", codecForString())
-    .property("bankBaseUrl", codecForString())
-    .property("exchangeBaseUrl", codecForString())
-    .build("WithdrawTestBalanceRequest");
+export const codecForWithdrawTestBalance =
+  (): Codec<WithdrawTestBalanceRequest> =>
+    buildCodecForObject<WithdrawTestBalanceRequest>()
+      .property("amount", codecForString())
+      .property("bankBaseUrl", codecForString())
+      .property("exchangeBaseUrl", codecForString())
+      .build("WithdrawTestBalanceRequest");
 
 export interface ApplyRefundResponse {
   contractTermsHash: string;
@@ -935,11 +950,12 @@ export interface SetCoinSuspendedRequest {
   suspended: boolean;
 }
 
-export const codecForSetCoinSuspendedRequest = (): 
Codec<SetCoinSuspendedRequest> =>
-  buildCodecForObject<SetCoinSuspendedRequest>()
-    .property("coinPub", codecForString())
-    .property("suspended", codecForBoolean())
-    .build("SetCoinSuspendedRequest");
+export const codecForSetCoinSuspendedRequest =
+  (): Codec<SetCoinSuspendedRequest> =>
+    buildCodecForObject<SetCoinSuspendedRequest>()
+      .property("coinPub", codecForString())
+      .property("suspended", codecForBoolean())
+      .build("SetCoinSuspendedRequest");
 
 export interface ForceRefreshRequest {
   coinPubList: string[];
@@ -972,10 +988,11 @@ export interface AbortPayWithRefundRequest {
   proposalId: string;
 }
 
-export const codecForAbortPayWithRefundRequest = (): 
Codec<AbortPayWithRefundRequest> =>
-  buildCodecForObject<AbortPayWithRefundRequest>()
-    .property("proposalId", codecForString())
-    .build("AbortPayWithRefundRequest");
+export const codecForAbortPayWithRefundRequest =
+  (): Codec<AbortPayWithRefundRequest> =>
+    buildCodecForObject<AbortPayWithRefundRequest>()
+      .property("proposalId", codecForString())
+      .build("AbortPayWithRefundRequest");
 
 export interface GetFeeForDepositRequest {
   depositPaytoUri: string;
@@ -987,18 +1004,18 @@ export interface CreateDepositGroupRequest {
   amount: AmountString;
 }
 
-
 export const codecForGetFeeForDeposit = (): Codec<GetFeeForDepositRequest> =>
   buildCodecForObject<GetFeeForDepositRequest>()
     .property("amount", codecForAmountString())
     .property("depositPaytoUri", codecForString())
     .build("GetFeeForDepositRequest");
 
-export const codecForCreateDepositGroupRequest = (): 
Codec<CreateDepositGroupRequest> =>
-  buildCodecForObject<CreateDepositGroupRequest>()
-    .property("amount", codecForAmountString())
-    .property("depositPaytoUri", codecForString())
-    .build("CreateDepositGroupRequest");
+export const codecForCreateDepositGroupRequest =
+  (): Codec<CreateDepositGroupRequest> =>
+    buildCodecForObject<CreateDepositGroupRequest>()
+      .property("amount", codecForAmountString())
+      .property("depositPaytoUri", codecForString())
+      .build("CreateDepositGroupRequest");
 
 export interface CreateDepositGroupResponse {
   depositGroupId: string;
@@ -1015,10 +1032,11 @@ export interface TrackDepositGroupResponse {
   }[];
 }
 
-export const codecForTrackDepositGroupRequest = (): 
Codec<TrackDepositGroupRequest> =>
-  buildCodecForObject<TrackDepositGroupRequest>()
-    .property("depositGroupId", codecForAmountString())
-    .build("TrackDepositGroupRequest");
+export const codecForTrackDepositGroupRequest =
+  (): Codec<TrackDepositGroupRequest> =>
+    buildCodecForObject<TrackDepositGroupRequest>()
+      .property("depositGroupId", codecForAmountString())
+      .build("TrackDepositGroupRequest");
 
 export interface WithdrawUriInfoResponse {
   amount: AmountString;
@@ -1026,12 +1044,13 @@ export interface WithdrawUriInfoResponse {
   possibleExchanges: ExchangeListItem[];
 }
 
-export const codecForWithdrawUriInfoResponse = (): 
Codec<WithdrawUriInfoResponse> =>
-  buildCodecForObject<WithdrawUriInfoResponse>()
-    .property("amount", codecForAmountString())
-    .property("defaultExchangeBaseUrl", codecOptional(codecForString()))
-    .property("possibleExchanges", codecForList(codecForExchangeListItem()))
-    .build("WithdrawUriInfoResponse");
+export const codecForWithdrawUriInfoResponse =
+  (): Codec<WithdrawUriInfoResponse> =>
+    buildCodecForObject<WithdrawUriInfoResponse>()
+      .property("amount", codecForAmountString())
+      .property("defaultExchangeBaseUrl", codecOptional(codecForString()))
+      .property("possibleExchanges", codecForList(codecForExchangeListItem()))
+      .build("WithdrawUriInfoResponse");
 
 export interface WalletCurrencyInfo {
   trustedAuditors: {
@@ -1054,15 +1073,17 @@ export interface RetryTransactionRequest {
   transactionId: string;
 }
 
-export const codecForDeleteTransactionRequest = (): 
Codec<DeleteTransactionRequest> =>
-  buildCodecForObject<DeleteTransactionRequest>()
-    .property("transactionId", codecForString())
-    .build("DeleteTransactionRequest");
+export const codecForDeleteTransactionRequest =
+  (): Codec<DeleteTransactionRequest> =>
+    buildCodecForObject<DeleteTransactionRequest>()
+      .property("transactionId", codecForString())
+      .build("DeleteTransactionRequest");
 
-export const codecForRetryTransactionRequest = (): 
Codec<RetryTransactionRequest> =>
-  buildCodecForObject<RetryTransactionRequest>()
-    .property("transactionId", codecForString())
-    .build("RetryTransactionRequest");
+export const codecForRetryTransactionRequest =
+  (): Codec<RetryTransactionRequest> =>
+    buildCodecForObject<RetryTransactionRequest>()
+      .property("transactionId", codecForString())
+      .build("RetryTransactionRequest");
 
 export interface SetWalletDeviceIdRequest {
   /**
@@ -1071,10 +1092,11 @@ export interface SetWalletDeviceIdRequest {
   walletDeviceId: string;
 }
 
-export const codecForSetWalletDeviceIdRequest = (): 
Codec<SetWalletDeviceIdRequest> =>
-  buildCodecForObject<SetWalletDeviceIdRequest>()
-    .property("walletDeviceId", codecForString())
-    .build("SetWalletDeviceIdRequest");
+export const codecForSetWalletDeviceIdRequest =
+  (): Codec<SetWalletDeviceIdRequest> =>
+    buildCodecForObject<SetWalletDeviceIdRequest>()
+      .property("walletDeviceId", codecForString())
+      .build("SetWalletDeviceIdRequest");
 
 export interface WithdrawFakebankRequest {
   amount: AmountString;
@@ -1082,12 +1104,13 @@ export interface WithdrawFakebankRequest {
   bank: string;
 }
 
-export const codecForWithdrawFakebankRequest = (): 
Codec<WithdrawFakebankRequest> =>
-  buildCodecForObject<WithdrawFakebankRequest>()
-    .property("amount", codecForAmountString())
-    .property("bank", codecForString())
-    .property("exchange", codecForString())
-    .build("WithdrawFakebankRequest");
+export const codecForWithdrawFakebankRequest =
+  (): Codec<WithdrawFakebankRequest> =>
+    buildCodecForObject<WithdrawFakebankRequest>()
+      .property("amount", codecForAmountString())
+      .property("bank", codecForString())
+      .property("exchange", codecForString())
+      .build("WithdrawFakebankRequest");
 
 export interface ImportDb {
   dump: any;
@@ -1095,4 +1118,4 @@ export interface ImportDb {
 export const codecForImportDbRequest = (): Codec<ImportDb> =>
   buildCodecForObject<ImportDb>()
     .property("dump", codecForAny())
-    .build("ImportDbRequest")
+    .build("ImportDbRequest");
diff --git a/packages/taler-wallet-cli/src/harness/harness.ts 
b/packages/taler-wallet-cli/src/harness/harness.ts
index fc489327..b4d2884d 100644
--- a/packages/taler-wallet-cli/src/harness/harness.ts
+++ b/packages/taler-wallet-cli/src/harness/harness.ts
@@ -1525,7 +1525,7 @@ export class ExchangeService implements 
ExchangeServiceInterface {
 
     this.exchangeHttpProc = this.globalState.spawnService(
       "taler-exchange-httpd",
-      ["-c", this.configFilename, ...this.timetravelArgArr],
+      ["-LINFO", "-c", this.configFilename, ...this.timetravelArgArr],
       `exchange-httpd-${this.name}`,
     );
 
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
index eceb26d7..ed07114e 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
@@ -201,8 +201,8 @@ export async function runExchangeTimetravelTest(t: 
GlobalTestState) {
   for (const da of denomPubs1) {
     let found = false;
     for (const db of denomPubs2) {
-      const d1 = DenominationPubKey.lift(da.denomPub);
-      const d2 = DenominationPubKey.lift(db.denomPub);
+      const d1 = da.denomPub;
+      const d2 = db.denomPub;
       if (DenominationPubKey.cmp(d1, d2) === 0) {
         found = true;
         break;
diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts 
b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
index 93a7cd1c..94abb8f7 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
@@ -30,8 +30,10 @@
 import {
   AmountJson,
   AmountString,
+  CoinEnvelope,
   DenominationPubKey,
   ExchangeProtocolVersion,
+  RefreshPlanchetInfo,
   UnblindedSignature,
 } from "@gnu-taler/taler-util";
 
@@ -74,32 +76,7 @@ export interface DerivedRefreshSession {
   /**
    * Planchets for each cut-and-choose instance.
    */
-  planchetsForGammas: {
-    /**
-     * Public key for the coin.
-     */
-    publicKey: string;
-
-    /**
-     * Private key for the coin.
-     */
-    privateKey: string;
-
-    /**
-     * Blinded public key.
-     */
-    coinEv: string;
-
-    /**
-     * Hash of the blinded public key.
-     */
-    coinEvHash: string;
-
-    /**
-     * Blinding key used.
-     */
-    blindingKey: string;
-  }[][];
+  planchetsForGammas: RefreshPlanchetInfo[][];
 
   /**
    * The transfer keys, kappa of them.
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts 
b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
index 29c2553a..16446bb9 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
@@ -28,6 +28,7 @@ import { CryptoWorker } from "./cryptoWorkerInterface.js";
 
 import {
   CoinDepositPermission,
+  CoinEnvelope,
   RecoupRefreshRequest,
   RecoupRequest,
 } from "@gnu-taler/taler-util";
@@ -452,7 +453,7 @@ export class CryptoApi {
     newDenomHash: string,
     oldCoinPub: string,
     transferPub: string,
-    coinEv: string,
+    coinEv: CoinEnvelope,
   ): Promise<string> {
     return this.doRpc<string>(
       "signCoinLink",
diff --git 
a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts 
b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index bff2e0eb..9f6d8234 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -26,19 +26,49 @@
 
 // FIXME: Crypto should not use DB Types!
 import {
-  AmountJson, Amounts, BenchmarkResult, buildSigPS,
-  CoinDepositPermission, createEddsaKeyPair, createHashContext, decodeCrock,
-  DenomKeyType, DepositInfo, eddsaGetPublic, eddsaSign, eddsaVerify,
-  encodeCrock, ExchangeProtocolVersion,
-  FreshCoin, hash, hashDenomPub, kdf, keyExchangeEcdheEddsa,
-  // Logger,
-  MakeSyncSignatureRequest, PlanchetCreationRequest, PlanchetCreationResult,
-  randomBytes, RecoupRefreshRequest,
+  AmountJson,
+  Amounts,
+  BenchmarkResult,
+  buildSigPS,
+  CoinDepositPermission,
+  CoinEnvelope,
+  createEddsaKeyPair,
+  createHashContext,
+  decodeCrock,
+  DenomKeyType,
+  DepositInfo,
+  eddsaGetPublic,
+  eddsaSign,
+  eddsaVerify,
+  encodeCrock,
+  ExchangeProtocolVersion,
+  FreshCoin,
+  hash,
+  HashCodeString,
+  hashCoinEv,
+  hashCoinEvInner,
+  hashDenomPub,
+  keyExchangeEcdheEddsa,
+  Logger,
+  MakeSyncSignatureRequest,
+  PlanchetCreationRequest,
+  PlanchetCreationResult,
+  randomBytes,
+  RecoupRefreshRequest,
   RecoupRequest,
-  RefreshPlanchetInfo, rsaBlind, rsaUnblind, rsaVerify, setupRefreshPlanchet,
+  RefreshPlanchetInfo,
+  rsaBlind,
+  rsaUnblind,
+  rsaVerify,
+  setupRefreshPlanchet,
   setupRefreshTransferPub,
   setupTipPlanchet,
-  setupWithdrawPlanchet, stringToBytes, TalerSignaturePurpose, Timestamp, 
timestampTruncateToSecond
+  setupWithdrawPlanchet,
+  stringToBytes,
+  TalerSignaturePurpose,
+  Timestamp,
+  timestampTruncateToSecond,
+  typedArrayConcat,
 } from "@gnu-taler/taler-util";
 import bigint from "big-integer";
 import { DenominationRecord, WireFee } from "../../db.js";
@@ -50,10 +80,10 @@ import {
   DerivedTipPlanchet,
   DeriveRefreshSessionRequest,
   DeriveTipRequest,
-  SignTrackTransactionRequest
+  SignTrackTransactionRequest,
 } from "../cryptoTypes.js";
 
-// const logger = new Logger("cryptoImplementation.ts");
+const logger = new Logger("cryptoImplementation.ts");
 
 function amountToBuffer(amount: AmountJson): Uint8Array {
   const buffer = new ArrayBuffer(8 + 4 + 12);
@@ -130,7 +160,7 @@ async function myEddsaSign(
 export class CryptoImplementation {
   static enableTracing = false;
 
-  constructor(private primitiveWorker?: PrimitiveWorker) { }
+  constructor(private primitiveWorker?: PrimitiveWorker) {}
 
   /**
    * Create a pre-coin of the given denomination to be withdrawn from then 
given
@@ -139,26 +169,26 @@ export class CryptoImplementation {
   async createPlanchet(
     req: PlanchetCreationRequest,
   ): Promise<PlanchetCreationResult> {
-    if (
-      req.denomPub.cipher === DenomKeyType.Rsa ||
-      req.denomPub.cipher === DenomKeyType.LegacyRsa
-    ) {
+    const denomPub = req.denomPub;
+    if (denomPub.cipher === DenomKeyType.Rsa) {
       const reservePub = decodeCrock(req.reservePub);
-      const denomPubRsa = decodeCrock(req.denomPub.rsa_public_key);
+      const denomPubRsa = decodeCrock(denomPub.rsa_public_key);
       const derivedPlanchet = setupWithdrawPlanchet(
         decodeCrock(req.secretSeed),
         req.coinIndex,
       );
       const coinPubHash = hash(derivedPlanchet.coinPub);
       const ev = rsaBlind(coinPubHash, derivedPlanchet.bks, denomPubRsa);
+      const coinEv: CoinEnvelope = {
+        cipher: DenomKeyType.Rsa,
+        rsa_blinded_planchet: encodeCrock(ev),
+      };
       const amountWithFee = Amounts.add(req.value, req.feeWithdraw).amount;
       const denomPubHash = hashDenomPub(req.denomPub);
-      const evHash = hash(ev);
-
+      const evHash = hashCoinEv(coinEv, encodeCrock(denomPubHash));
       const withdrawRequest = buildSigPS(
         TalerSignaturePurpose.WALLET_RESERVE_WITHDRAW,
       )
-        .put(reservePub)
         .put(amountToBuffer(amountWithFee))
         .put(denomPubHash)
         .put(evHash)
@@ -171,14 +201,11 @@ export class CryptoImplementation {
 
       const planchet: PlanchetCreationResult = {
         blindingKey: encodeCrock(derivedPlanchet.bks),
-        coinEv: encodeCrock(ev),
+        coinEv,
         coinPriv: encodeCrock(derivedPlanchet.coinPriv),
         coinPub: encodeCrock(derivedPlanchet.coinPub),
         coinValue: req.value,
-        denomPub: {
-          cipher: req.denomPub.cipher,
-          rsa_public_key: encodeCrock(denomPubRsa),
-        },
+        denomPub,
         denomPubHash: encodeCrock(denomPubHash),
         reservePub: encodeCrock(reservePub),
         withdrawSig: sigResult.sig,
@@ -194,11 +221,8 @@ export class CryptoImplementation {
    * Create a planchet used for tipping, including the private keys.
    */
   createTipPlanchet(req: DeriveTipRequest): DerivedTipPlanchet {
-    if (
-      req.denomPub.cipher !== DenomKeyType.Rsa &&
-      req.denomPub.cipher !== DenomKeyType.LegacyRsa
-    ) {
-      throw Error("unsupported cipher");
+    if (req.denomPub.cipher !== DenomKeyType.Rsa) {
+      throw Error(`unsupported cipher (${req.denomPub.cipher})`);
     }
     const fc = setupTipPlanchet(decodeCrock(req.secretSeed), 
req.planchetIndex);
     const denomPub = decodeCrock(req.denomPub.rsa_public_key);
@@ -236,15 +260,7 @@ export class CryptoImplementation {
 
     const coinPriv = decodeCrock(req.coinPriv);
     const coinSig = eddsaSign(p, coinPriv);
-    if (req.denomPub.cipher === DenomKeyType.LegacyRsa) {
-      const paybackRequest: RecoupRequest = {
-        coin_blind_key_secret: req.blindingKey,
-        coin_sig: encodeCrock(coinSig),
-        denom_pub_hash: req.denomPubHash,
-        denom_sig: req.denomSig.rsa_signature,
-      };
-      return paybackRequest;
-    } else {
+    if (req.denomPub.cipher === DenomKeyType.Rsa) {
       const paybackRequest: RecoupRequest = {
         coin_blind_key_secret: req.blindingKey,
         coin_sig: encodeCrock(coinSig),
@@ -252,6 +268,8 @@ export class CryptoImplementation {
         denom_sig: req.denomSig,
       };
       return paybackRequest;
+    } else {
+      throw new Error();
     }
   }
 
@@ -268,15 +286,7 @@ export class CryptoImplementation {
 
     const coinPriv = decodeCrock(req.coinPriv);
     const coinSig = eddsaSign(p, coinPriv);
-    if (req.denomPub.cipher === DenomKeyType.LegacyRsa) {
-      const recoupRequest: RecoupRefreshRequest = {
-        coin_blind_key_secret: req.blindingKey,
-        coin_sig: encodeCrock(coinSig),
-        denom_pub_hash: req.denomPubHash,
-        denom_sig: req.denomSig.rsa_signature,
-      };
-      return recoupRequest;
-    } else {
+    if (req.denomPub.cipher === DenomKeyType.Rsa) {
       const recoupRequest: RecoupRefreshRequest = {
         coin_blind_key_secret: req.blindingKey,
         coin_sig: encodeCrock(coinSig),
@@ -284,6 +294,8 @@ export class CryptoImplementation {
         denom_sig: req.denomSig,
       };
       return recoupRequest;
+    } else {
+      throw new Error();
     }
   }
 
@@ -364,26 +376,11 @@ export class CryptoImplementation {
     sig: string,
     masterPub: string,
   ): boolean {
-    if (versionCurrent === ExchangeProtocolVersion.V12) {
-      const paytoHash = hash(stringToBytes(paytoUri + "\0"));
-      const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
-        .put(paytoHash)
-        .build();
-      return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
-    } else if (versionCurrent === ExchangeProtocolVersion.V9) {
-      const h = kdf(
-        64,
-        stringToBytes("exchange-wire-signature"),
-        stringToBytes(paytoUri + "\0"),
-        new Uint8Array(0),
-      );
-      const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
-        .put(h)
-        .build();
-      return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
-    } else {
-      throw Error(`unsupported version (${versionCurrent})`);
-    }
+    const paytoHash = hash(stringToBytes(paytoUri + "\0"));
+    const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
+      .put(paytoHash)
+      .build();
+    return eddsaVerify(p, decodeCrock(sig), decodeCrock(masterPub));
   }
 
   isValidContractTermsSignature(
@@ -444,10 +441,12 @@ export class CryptoImplementation {
   ): Promise<CoinDepositPermission> {
     // FIXME: put extensions here if used
     const hExt = new Uint8Array(64);
+    const hAgeCommitment = new Uint8Array(32);
     let d: Uint8Array;
     if (depositInfo.denomKeyType === DenomKeyType.Rsa) {
       d = buildSigPS(TalerSignaturePurpose.WALLET_COIN_DEPOSIT)
         .put(decodeCrock(depositInfo.contractTermsHash))
+        .put(hAgeCommitment)
         .put(hExt)
         .put(decodeCrock(depositInfo.wireInfoHash))
         .put(decodeCrock(depositInfo.denomPubHash))
@@ -457,18 +456,6 @@ export class CryptoImplementation {
         .put(amountToBuffer(depositInfo.feeDeposit))
         .put(decodeCrock(depositInfo.merchantPub))
         .build();
-    } else if (depositInfo.denomKeyType === DenomKeyType.LegacyRsa) {
-      d = buildSigPS(TalerSignaturePurpose.WALLET_COIN_DEPOSIT)
-        .put(decodeCrock(depositInfo.contractTermsHash))
-        .put(decodeCrock(depositInfo.wireInfoHash))
-        .put(decodeCrock(depositInfo.denomPubHash))
-        .put(timestampRoundedToBuffer(depositInfo.timestamp))
-        .put(timestampRoundedToBuffer(depositInfo.refundDeadline))
-        .put(amountToBuffer(depositInfo.spendAmount))
-        .put(amountToBuffer(depositInfo.feeDeposit))
-        .put(decodeCrock(depositInfo.merchantPub))
-        .put(decodeCrock(depositInfo.coinPub))
-        .build();
     } else {
       throw Error("unsupported exchange protocol version");
     }
@@ -490,18 +477,10 @@ export class CryptoImplementation {
         },
       };
       return s;
-    } else if (depositInfo.denomKeyType === DenomKeyType.LegacyRsa) {
-      const s: CoinDepositPermission = {
-        coin_pub: depositInfo.coinPub,
-        coin_sig: coinSigRes.sig,
-        contribution: Amounts.stringify(depositInfo.spendAmount),
-        h_denom: depositInfo.denomPubHash,
-        exchange_url: depositInfo.exchangeBaseUrl,
-        ub_sig: depositInfo.denomSig.rsa_signature,
-      };
-      return s;
     } else {
-      throw Error("unsupported merchant protocol version");
+      throw Error(
+        `unsupported denomination cipher (${depositInfo.denomKeyType})`,
+      );
     }
   }
 
@@ -551,17 +530,18 @@ export class CryptoImplementation {
 
     for (const denomSel of newCoinDenoms) {
       for (let i = 0; i < denomSel.count; i++) {
-        if (denomSel.denomPub.cipher === DenomKeyType.LegacyRsa) {
-          const r = decodeCrock(denomSel.denomPub.rsa_public_key);
-          sessionHc.update(r);
+        if (denomSel.denomPub.cipher === DenomKeyType.Rsa) {
+          const denomPubHash = hashDenomPub(denomSel.denomPub);
+          sessionHc.update(denomPubHash);
         } else {
-          sessionHc.update(hashDenomPub(denomSel.denomPub));
+          throw new Error();
         }
       }
     }
 
     sessionHc.update(decodeCrock(meltCoinPub));
     sessionHc.update(amountToBuffer(valueWithFee));
+
     for (let i = 0; i < kappa; i++) {
       const planchets: RefreshPlanchetInfo[] = [];
       for (let j = 0; j < newCoinDenoms.length; j++) {
@@ -594,24 +574,29 @@ export class CryptoImplementation {
             coinPub = fresh.coinPub;
             blindingFactor = fresh.bks;
           }
-          const pubHash = hash(coinPub);
-          if (
-            denomSel.denomPub.cipher !== DenomKeyType.Rsa &&
-            denomSel.denomPub.cipher !== DenomKeyType.LegacyRsa
-          ) {
+          const coinPubHash = hash(coinPub);
+          if (denomSel.denomPub.cipher !== DenomKeyType.Rsa) {
             throw Error("unsupported cipher, can't create refresh session");
           }
-          const denomPub = decodeCrock(denomSel.denomPub.rsa_public_key);
-          const ev = rsaBlind(pubHash, blindingFactor, denomPub);
+          const rsaDenomPub = decodeCrock(denomSel.denomPub.rsa_public_key);
+          const ev = rsaBlind(coinPubHash, blindingFactor, rsaDenomPub);
+          const coinEv: CoinEnvelope = {
+            cipher: DenomKeyType.Rsa,
+            rsa_blinded_planchet: encodeCrock(ev),
+          };
+          const coinEvHash = hashCoinEv(
+            coinEv,
+            encodeCrock(hashDenomPub(denomSel.denomPub)),
+          );
           const planchet: RefreshPlanchetInfo = {
             blindingKey: encodeCrock(blindingFactor),
-            coinEv: encodeCrock(ev),
-            privateKey: encodeCrock(coinPriv),
-            publicKey: encodeCrock(coinPub),
-            coinEvHash: encodeCrock(hash(ev)),
+            coinEv,
+            coinPriv: encodeCrock(coinPriv),
+            coinPub: encodeCrock(coinPub),
+            coinEvHash: encodeCrock(coinEvHash),
           };
           planchets.push(planchet);
-          sessionHc.update(ev);
+          hashCoinEvInner(coinEv, sessionHc);
         }
       }
       planchetsForGammas.push(planchets);
@@ -619,26 +604,15 @@ export class CryptoImplementation {
 
     const sessionHash = sessionHc.finish();
     let confirmData: Uint8Array;
-    if (req.exchangeProtocolVersion === ExchangeProtocolVersion.V9) {
-      confirmData = buildSigPS(TalerSignaturePurpose.WALLET_COIN_MELT)
-        .put(sessionHash)
-        .put(decodeCrock(meltCoinDenomPubHash))
-        .put(amountToBuffer(valueWithFee))
-        .put(amountToBuffer(meltFee))
-        .put(decodeCrock(meltCoinPub))
-        .build();
-    } else if (req.exchangeProtocolVersion === ExchangeProtocolVersion.V12) {
-      confirmData = buildSigPS(TalerSignaturePurpose.WALLET_COIN_MELT)
-        .put(sessionHash)
-        .put(decodeCrock(meltCoinDenomPubHash))
-        .put(amountToBuffer(valueWithFee))
-        .put(amountToBuffer(meltFee))
-        .build();
-    } else {
-      throw Error(
-        `Exchange protocol version (${req.exchangeProtocolVersion}) not 
supported`,
-      );
-    }
+    // FIXME: fill in age commitment
+    const hAgeCommitment = new Uint8Array(32);
+    confirmData = buildSigPS(TalerSignaturePurpose.WALLET_COIN_MELT)
+      .put(sessionHash)
+      .put(decodeCrock(meltCoinDenomPubHash))
+      .put(hAgeCommitment)
+      .put(amountToBuffer(valueWithFee))
+      .put(amountToBuffer(meltFee))
+      .build();
 
     const confirmSigResp = await myEddsaSign(this.primitiveWorker, {
       msg: encodeCrock(confirmData),
@@ -678,12 +652,15 @@ export class CryptoImplementation {
     newDenomHash: string,
     oldCoinPub: string,
     transferPub: string,
-    coinEv: string,
+    coinEv: CoinEnvelope,
   ): Promise<string> {
-    const coinEvHash = hash(decodeCrock(coinEv));
+    const coinEvHash = hashCoinEv(coinEv, newDenomHash);
+    // FIXME: fill in
+    const hAgeCommitment = new Uint8Array(32);
     const coinLink = buildSigPS(TalerSignaturePurpose.WALLET_COIN_LINK)
       .put(decodeCrock(newDenomHash))
       .put(decodeCrock(transferPub))
+      .put(hAgeCommitment)
       .put(coinEvHash)
       .build();
     const sig = await myEddsaSign(this.primitiveWorker, {
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index 7f7dd10f..41031153 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -38,6 +38,7 @@ import {
   TalerErrorDetails,
   Timestamp,
   UnblindedSignature,
+  CoinEnvelope,
 } from "@gnu-taler/taler-util";
 import { RetryInfo } from "./util/retries.js";
 import { PayCoinSelection } from "./util/coinSelection.js";
@@ -602,7 +603,7 @@ export interface PlanchetRecord {
 
   withdrawSig: string;
 
-  coinEv: string;
+  coinEv: CoinEnvelope;
 
   coinEvHash: string;
 
@@ -1154,7 +1155,6 @@ export interface WalletContractData {
   timestamp: Timestamp;
   wireMethod: string;
   wireInfoHash: string;
-  wireInfoLegacyHash?: string;
   maxDepositFee: AmountJson;
 }
 
@@ -1294,9 +1294,9 @@ export const WALLET_BACKUP_STATE_KEY = 
"walletBackupState";
  */
 export type ConfigRecord =
   | {
-    key: typeof WALLET_BACKUP_STATE_KEY;
-    value: WalletBackupConfState;
-  }
+      key: typeof WALLET_BACKUP_STATE_KEY;
+      value: WalletBackupConfState;
+    }
   | { key: "currencyDefaultsApplied"; value: boolean };
 
 export interface WalletBackupConfState {
@@ -1392,9 +1392,9 @@ export interface WithdrawalGroupRecord {
 
   /**
    * UID of the denomination selection.
-   * 
+   *
    * Used for merging backups.
-   * 
+   *
    * FIXME: Should this not also include a timestamp for more logical merging?
    */
   denomSelUid: string;
@@ -1480,17 +1480,17 @@ export enum BackupProviderStateTag {
 
 export type BackupProviderState =
   | {
-    tag: BackupProviderStateTag.Provisional;
-  }
+      tag: BackupProviderStateTag.Provisional;
+    }
   | {
-    tag: BackupProviderStateTag.Ready;
-    nextBackupTimestamp: Timestamp;
-  }
+      tag: BackupProviderStateTag.Ready;
+      nextBackupTimestamp: Timestamp;
+    }
   | {
-    tag: BackupProviderStateTag.Retrying;
-    retryInfo: RetryInfo;
-    lastError?: TalerErrorDetails;
-  };
+      tag: BackupProviderStateTag.Retrying;
+      retryInfo: RetryInfo;
+      lastError?: TalerErrorDetails;
+    };
 
 export interface BackupProviderTerms {
   supportedProtocolVersion: string;
@@ -1875,9 +1875,9 @@ export function exportDb(db: IDBDatabase): Promise<any> {
 }
 
 export interface DatabaseDump {
-  name: string,
-  stores: { [s: string]: any },
-  version: string,
+  name: string;
+  stores: { [s: string]: any };
+  version: string;
 }
 
 export function importDb(db: IDBDatabase, dump: DatabaseDump): Promise<any> {
@@ -1891,12 +1891,11 @@ export function importDb(db: IDBDatabase, dump: 
DatabaseDump): Promise<any> {
       const name = db.objectStoreNames[i];
       const storeDump = dump.stores[name];
       if (!storeDump) continue;
-      Object.keys(storeDump).forEach(async key => {
-        const value = storeDump[key]
+      Object.keys(storeDump).forEach(async (key) => {
+        const value = storeDump[key];
         if (!value) return;
-        tx.objectStore(name).put(value)
-      })
-
+        tx.objectStore(name).put(value);
+      });
     }
   });
 }
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts 
b/packages/taler-wallet-core/src/operations/backup/import.ts
index 9f63441d..21b10a94 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -337,8 +337,7 @@ export async function importBackup(
 
         for (const backupDenomination of backupExchangeDetails.denominations) {
           if (
-            backupDenomination.denom_pub.cipher !== DenomKeyType.Rsa &&
-            backupDenomination.denom_pub.cipher !== DenomKeyType.LegacyRsa
+            backupDenomination.denom_pub.cipher !== DenomKeyType.Rsa
           ) {
             throw Error("unsupported cipher");
           }
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts 
b/packages/taler-wallet-core/src/operations/backup/index.ts
index e3950ef9..5eb24861 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -168,10 +168,7 @@ async function computeBackupCryptoData(
   };
   for (const backupExchangeDetails of backupContent.exchange_details) {
     for (const backupDenom of backupExchangeDetails.denominations) {
-      if (
-        backupDenom.denom_pub.cipher !== DenomKeyType.Rsa &&
-        backupDenom.denom_pub.cipher !== DenomKeyType.LegacyRsa
-      ) {
+      if (backupDenom.denom_pub.cipher !== DenomKeyType.Rsa) {
         throw Error("unsupported cipher");
       }
       for (const backupCoin of backupDenom.coins) {
@@ -192,18 +189,14 @@ async function computeBackupCryptoData(
         LibtoolVersion.compare(backupExchangeDetails.protocol_version, "9")
           ?.compatible
       ) {
-        cryptoData.rsaDenomPubToHash[
-          backupDenom.denom_pub.rsa_public_key
-        ] = encodeCrock(
-          hash(decodeCrock(backupDenom.denom_pub.rsa_public_key)),
-        );
+        cryptoData.rsaDenomPubToHash[backupDenom.denom_pub.rsa_public_key] =
+          encodeCrock(hash(decodeCrock(backupDenom.denom_pub.rsa_public_key)));
       } else if (
         LibtoolVersion.compare(backupExchangeDetails.protocol_version, "10")
           ?.compatible
       ) {
-        cryptoData.rsaDenomPubToHash[
-          backupDenom.denom_pub.rsa_public_key
-        ] = encodeCrock(hashDenomPub(backupDenom.denom_pub));
+        cryptoData.rsaDenomPubToHash[backupDenom.denom_pub.rsa_public_key] =
+          encodeCrock(hashDenomPub(backupDenom.denom_pub));
       } else {
         throw Error("unsupported exchange protocol version");
       }
@@ -220,9 +213,8 @@ async function computeBackupCryptoData(
     );
     const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(prop.nonce_priv)));
     cryptoData.proposalNoncePrivToPub[prop.nonce_priv] = noncePub;
-    cryptoData.proposalIdToContractTermsHash[
-      prop.proposal_id
-    ] = contractTermsHash;
+    cryptoData.proposalIdToContractTermsHash[prop.proposal_id] =
+      contractTermsHash;
   }
   for (const purch of backupContent.purchases) {
     const contractTermsHash = await cryptoApi.hashString(
@@ -230,9 +222,8 @@ async function computeBackupCryptoData(
     );
     const noncePub = 
encodeCrock(eddsaGetPublic(decodeCrock(purch.nonce_priv)));
     cryptoData.proposalNoncePrivToPub[purch.nonce_priv] = noncePub;
-    cryptoData.proposalIdToContractTermsHash[
-      purch.proposal_id
-    ] = contractTermsHash;
+    cryptoData.proposalIdToContractTermsHash[purch.proposal_id] =
+      contractTermsHash;
   }
   return cryptoData;
 }
@@ -548,10 +539,11 @@ export interface RemoveBackupProviderRequest {
   provider: string;
 }
 
-export const codecForRemoveBackupProvider = (): 
Codec<RemoveBackupProviderRequest> =>
-  buildCodecForObject<RemoveBackupProviderRequest>()
-    .property("provider", codecForString())
-    .build("RemoveBackupProviderRequest");
+export const codecForRemoveBackupProvider =
+  (): Codec<RemoveBackupProviderRequest> =>
+    buildCodecForObject<RemoveBackupProviderRequest>()
+      .property("provider", codecForString())
+      .build("RemoveBackupProviderRequest");
 
 export async function removeBackupProvider(
   ws: InternalWalletState,
@@ -619,12 +611,13 @@ interface SyncTermsOfServiceResponse {
   version: string;
 }
 
-const codecForSyncTermsOfServiceResponse = (): 
Codec<SyncTermsOfServiceResponse> =>
-  buildCodecForObject<SyncTermsOfServiceResponse>()
-    .property("storage_limit_in_megabytes", codecForNumber())
-    .property("annual_fee", codecForAmountString())
-    .property("version", codecForString())
-    .build("SyncTermsOfServiceResponse");
+const codecForSyncTermsOfServiceResponse =
+  (): Codec<SyncTermsOfServiceResponse> =>
+    buildCodecForObject<SyncTermsOfServiceResponse>()
+      .property("storage_limit_in_megabytes", codecForNumber())
+      .property("annual_fee", codecForAmountString())
+      .property("version", codecForString())
+      .build("SyncTermsOfServiceResponse");
 
 export interface AddBackupProviderRequest {
   backupProviderBaseUrl: string;
@@ -637,12 +630,13 @@ export interface AddBackupProviderRequest {
   activate?: boolean;
 }
 
-export const codecForAddBackupProviderRequest = (): 
Codec<AddBackupProviderRequest> =>
-  buildCodecForObject<AddBackupProviderRequest>()
-    .property("backupProviderBaseUrl", codecForString())
-    .property("name", codecForString())
-    .property("activate", codecOptional(codecForBoolean()))
-    .build("AddBackupProviderRequest");
+export const codecForAddBackupProviderRequest =
+  (): Codec<AddBackupProviderRequest> =>
+    buildCodecForObject<AddBackupProviderRequest>()
+      .property("backupProviderBaseUrl", codecForString())
+      .property("name", codecForString())
+      .property("activate", codecOptional(codecForBoolean()))
+      .build("AddBackupProviderRequest");
 
 export async function addBackupProvider(
   ws: InternalWalletState,
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts 
b/packages/taler-wallet-core/src/operations/deposits.ts
index 8a5b3573..25b9cb92 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -27,7 +27,11 @@ import {
   CreateDepositGroupRequest,
   CreateDepositGroupResponse,
   DenomKeyType,
-  durationFromSpec, encodeCrock, GetFeeForDepositRequest, getRandomBytes, 
getTimestampNow,
+  durationFromSpec,
+  encodeCrock,
+  GetFeeForDepositRequest,
+  getRandomBytes,
+  getTimestampNow,
   Logger,
   NotificationType,
   parsePaytoUri,
@@ -38,7 +42,7 @@ import {
   timestampTruncateToSecond,
   TrackDepositGroupRequest,
   TrackDepositGroupResponse,
-  URL
+  URL,
 } from "@gnu-taler/taler-util";
 import { InternalWalletState } from "../common.js";
 import { DepositGroupRecord, OperationStatus } from "../db.js";
@@ -54,7 +58,7 @@ import {
   getCandidatePayCoins,
   getTotalPaymentCost,
   hashWire,
-  hashWireLegacy
+  hashWireLegacy,
 } from "./pay.js";
 import { getTotalRefreshCost } from "./refresh.js";
 
@@ -199,47 +203,21 @@ async function processDepositGroupImpl(
     }
     const perm = depositPermissions[i];
     let requestBody: any;
-    if (
-      typeof perm.ub_sig === "string" ||
-      perm.ub_sig.cipher === DenomKeyType.LegacyRsa
-    ) {
-      // Legacy request
-      logger.info("creating legacy deposit request");
-      const wireHash = hashWireLegacy(
-        depositGroup.wire.payto_uri,
-        depositGroup.wire.salt,
-      );
-      requestBody = {
-        contribution: Amounts.stringify(perm.contribution),
-        wire: depositGroup.wire,
-        h_wire: wireHash,
-        h_contract_terms: depositGroup.contractTermsHash,
-        ub_sig: perm.ub_sig,
-        timestamp: depositGroup.contractTermsRaw.timestamp,
-        wire_transfer_deadline:
-          depositGroup.contractTermsRaw.wire_transfer_deadline,
-        refund_deadline: depositGroup.contractTermsRaw.refund_deadline,
-        coin_sig: perm.coin_sig,
-        denom_pub_hash: perm.h_denom,
-        merchant_pub: depositGroup.merchantPub,
-      };
-    } else {
-      logger.info("creating v10 deposit request");
-      requestBody = {
-        contribution: Amounts.stringify(perm.contribution),
-        merchant_payto_uri: depositGroup.wire.payto_uri,
-        wire_salt: depositGroup.wire.salt,
-        h_contract_terms: depositGroup.contractTermsHash,
-        ub_sig: perm.ub_sig,
-        timestamp: depositGroup.contractTermsRaw.timestamp,
-        wire_transfer_deadline:
-          depositGroup.contractTermsRaw.wire_transfer_deadline,
-        refund_deadline: depositGroup.contractTermsRaw.refund_deadline,
-        coin_sig: perm.coin_sig,
-        denom_pub_hash: perm.h_denom,
-        merchant_pub: depositGroup.merchantPub,
-      };
-    }
+    logger.info("creating v10 deposit request");
+    requestBody = {
+      contribution: Amounts.stringify(perm.contribution),
+      merchant_payto_uri: depositGroup.wire.payto_uri,
+      wire_salt: depositGroup.wire.salt,
+      h_contract_terms: depositGroup.contractTermsHash,
+      ub_sig: perm.ub_sig,
+      timestamp: depositGroup.contractTermsRaw.timestamp,
+      wire_transfer_deadline:
+        depositGroup.contractTermsRaw.wire_transfer_deadline,
+      refund_deadline: depositGroup.contractTermsRaw.refund_deadline,
+      coin_sig: perm.coin_sig,
+      denom_pub_hash: perm.h_denom,
+      merchant_pub: depositGroup.merchantPub,
+    };
     const url = new URL(`coins/${perm.coin_pub}/deposit`, perm.exchange_url);
     logger.info(`depositing to ${url}`);
     const httpResp = await ws.http.postJson(url.href, requestBody);
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 87200c2f..c50afc21 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -83,15 +83,7 @@ function denominationRecordFromKeys(
   denomIn: ExchangeDenomination,
 ): DenominationRecord {
   let denomPub: DenominationPubKey;
-  // We support exchange protocol v9 and v10.
-  if (typeof denomIn.denom_pub === "string") {
-    denomPub = {
-      cipher: DenomKeyType.LegacyRsa,
-      rsa_public_key: denomIn.denom_pub,
-    };
-  } else {
-    denomPub = denomIn.denom_pub;
-  }
+  denomPub = denomIn.denom_pub;
   const denomPubHash = encodeCrock(hashDenomPub(denomPub));
   const d: DenominationRecord = {
     denomPub,
diff --git a/packages/taler-wallet-core/src/operations/pay.ts 
b/packages/taler-wallet-core/src/operations/pay.ts
index 8f0727c8..4870d446 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -606,7 +606,6 @@ export function extractContractData(
     timestamp: parsedContractTerms.timestamp,
     wireMethod: parsedContractTerms.wire_method,
     wireInfoHash: parsedContractTerms.h_wire,
-    wireInfoLegacyHash: parsedContractTerms.h_wire_legacy,
     maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee),
     merchant: parsedContractTerms.merchant,
     products: parsedContractTerms.products,
@@ -1515,14 +1514,7 @@ export async function generateDepositPermissions(
   for (let i = 0; i < payCoinSel.coinPubs.length; i++) {
     const { coin, denom } = coinWithDenom[i];
     let wireInfoHash: string;
-    if (
-      coin.denomPub.cipher === DenomKeyType.LegacyRsa &&
-      contractData.wireInfoLegacyHash
-    ) {
-      wireInfoHash = contractData.wireInfoLegacyHash;
-    } else {
-      wireInfoHash = contractData.wireInfoHash;
-    }
+    wireInfoHash = contractData.wireInfoHash;
     const dp = await ws.cryptoApi.signDepositPermission({
       coinPriv: coin.coinPriv,
       coinPub: coin.coinPub,
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index 1e5dd68a..ba4cb697 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -18,8 +18,10 @@ import {
   DenomKeyType,
   encodeCrock,
   ExchangeProtocolVersion,
+  ExchangeRefreshRevealRequest,
   getRandomBytes,
   HttpStatusCode,
+  j2s,
 } from "@gnu-taler/taler-util";
 import {
   CoinRecord,
@@ -369,10 +371,6 @@ async function refreshMelt(
 
   let exchangeProtocolVersion: ExchangeProtocolVersion;
   switch (d.oldDenom.denomPub.cipher) {
-    case DenomKeyType.LegacyRsa: {
-      exchangeProtocolVersion = ExchangeProtocolVersion.V9;
-      break;
-    }
     case DenomKeyType.Rsa: {
       exchangeProtocolVersion = ExchangeProtocolVersion.V12;
       break;
@@ -397,16 +395,7 @@ async function refreshMelt(
     oldCoin.exchangeBaseUrl,
   );
   let meltReqBody: any;
-  if (oldCoin.denomPub.cipher === DenomKeyType.LegacyRsa) {
-    meltReqBody = {
-      coin_pub: oldCoin.coinPub,
-      confirm_sig: derived.confirmSig,
-      denom_pub_hash: oldCoin.denomPubHash,
-      denom_sig: oldCoin.denomSig.rsa_signature,
-      rc: derived.hash,
-      value_with_fee: Amounts.stringify(derived.meltValueWithFee),
-    };
-  } else {
+  if (oldCoin.denomPub.cipher === DenomKeyType.Rsa) {
     meltReqBody = {
       coin_pub: oldCoin.coinPub,
       confirm_sig: derived.confirmSig,
@@ -569,10 +558,6 @@ async function refreshReveal(
 
   let exchangeProtocolVersion: ExchangeProtocolVersion;
   switch (d.oldDenom.denomPub.cipher) {
-    case DenomKeyType.LegacyRsa: {
-      exchangeProtocolVersion = ExchangeProtocolVersion.V9;
-      break;
-    }
     case DenomKeyType.Rsa: {
       exchangeProtocolVersion = ExchangeProtocolVersion.V12;
       break;
@@ -600,7 +585,6 @@ async function refreshReveal(
     throw Error("refresh index error");
   }
 
-  const evs = planchets.map((x: RefreshPlanchetInfo) => x.coinEv);
   const newDenomsFlat: string[] = [];
   const linkSigs: string[] = [];
 
@@ -620,10 +604,9 @@ async function refreshReveal(
     }
   }
 
-  const req = {
-    coin_evs: evs,
+  const req: ExchangeRefreshRevealRequest = {
+    coin_evs: planchets.map((x) => x.coinEv),
     new_denoms_h: newDenomsFlat,
-    rc: derived.hash,
     transfer_privs: privs,
     transfer_pub: derived.transferPubs[norevealIndex],
     link_sigs: linkSigs,
@@ -666,20 +649,14 @@ async function refreshReveal(
         continue;
       }
       const pc = derived.planchetsForGammas[norevealIndex][newCoinIndex];
-      if (
-        denom.denomPub.cipher !== DenomKeyType.Rsa &&
-        denom.denomPub.cipher !== DenomKeyType.LegacyRsa
-      ) {
+      if (denom.denomPub.cipher !== DenomKeyType.Rsa) {
         throw Error("cipher unsupported");
       }
       const evSig = reveal.ev_sigs[newCoinIndex].ev_sig;
       let rsaSig: string;
       if (typeof evSig === "string") {
         rsaSig = evSig;
-      } else if (
-        evSig.cipher === DenomKeyType.Rsa ||
-        evSig.cipher === DenomKeyType.LegacyRsa
-      ) {
+      } else if (evSig.cipher === DenomKeyType.Rsa) {
         rsaSig = evSig.blinded_rsa_signature;
       } else {
         throw Error("unsupported cipher");
@@ -691,8 +668,8 @@ async function refreshReveal(
       );
       const coin: CoinRecord = {
         blindingKey: pc.blindingKey,
-        coinPriv: pc.privateKey,
-        coinPub: pc.publicKey,
+        coinPriv: pc.coinPriv,
+        coinPub: pc.coinPub,
         currentAmount: denom.value,
         denomPub: denom.denomPub,
         denomPubHash: denom.denomPubHash,
@@ -707,7 +684,7 @@ async function refreshReveal(
           oldCoinPub: refreshGroup.oldCoinPubs[coinIndex],
         },
         suspended: false,
-        coinEvHash: pc.coinEv,
+        coinEvHash: pc.coinEvHash,
       };
 
       coins.push(coin);
diff --git a/packages/taler-wallet-core/src/operations/tip.ts 
b/packages/taler-wallet-core/src/operations/tip.ts
index f985d8aa..039fb64a 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -306,37 +306,13 @@ async function processTipImpl(
     // FIXME: Maybe we want to signal to the caller that the transient error 
happened?
     return;
   }
-
-  // FIXME: Do this earlier?
-  const merchantInfo = await ws.merchantOps.getMerchantInfo(
-    ws,
-    tipRecord.merchantBaseUrl,
-  );
-
   let blindedSigs: BlindedDenominationSignature[] = [];
 
-  if (merchantInfo.protocolVersionCurrent === MerchantProtocolVersion.V3) {
-    const response = await readSuccessResponseJsonOrThrow(
-      merchantResp,
-      codecForMerchantTipResponseV2(),
-    );
-    blindedSigs = response.blind_sigs.map((x) => x.blind_sig);
-  } else if (
-    merchantInfo.protocolVersionCurrent === MerchantProtocolVersion.V1
-  ) {
-    const response = await readSuccessResponseJsonOrThrow(
-      merchantResp,
-      codecForMerchantTipResponseV1(),
-    );
-    blindedSigs = response.blind_sigs.map((x) => ({
-      cipher: DenomKeyType.Rsa,
-      blinded_rsa_signature: x.blind_sig,
-    }));
-  } else {
-    throw Error(
-      `unsupported merchant protocol version 
(${merchantInfo.protocolVersionCurrent})`,
-    );
-  }
+  const response = await readSuccessResponseJsonOrThrow(
+    merchantResp,
+    codecForMerchantTipResponseV2(),
+  );
+  blindedSigs = response.blind_sigs.map((x) => x.blind_sig);
 
   if (blindedSigs.length !== planchets.length) {
     throw Error("number of tip responses does not match requested planchets");
@@ -352,17 +328,11 @@ async function processTipImpl(
     const planchet = planchets[i];
     checkLogicInvariant(!!planchet);
 
-    if (
-      denom.denomPub.cipher !== DenomKeyType.Rsa &&
-      denom.denomPub.cipher !== DenomKeyType.LegacyRsa
-    ) {
+    if (denom.denomPub.cipher !== DenomKeyType.Rsa) {
       throw Error("unsupported cipher");
     }
 
-    if (
-      blindedSig.cipher !== DenomKeyType.Rsa &&
-      blindedSig.cipher !== DenomKeyType.LegacyRsa
-    ) {
+    if (blindedSig.cipher !== DenomKeyType.Rsa) {
       throw Error("unsupported cipher");
     }
 
diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts 
b/packages/taler-wallet-core/src/operations/withdraw.test.ts
index 2c890a12..02540848 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.test.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts } from "@gnu-taler/taler-util";
+import { Amounts, DenomKeyType } from "@gnu-taler/taler-util";
 import test from "ava";
 import { DenominationRecord, DenominationVerificationStatus } from "../db.js";
 import { selectWithdrawalDenominations } from "./withdraw.js";
@@ -29,7 +29,7 @@ test("withdrawal selection bug repro", (t) => {
   const denoms: DenominationRecord[] = [
     {
       denomPub: {
-        cipher: 1,
+        cipher: DenomKeyType.Rsa,
         rsa_public_key:
           
"040000XT67C8KBD6B75TTQ3SK8FWXMNQW4372T3BDDGPAMB9RFCA03638W8T3F71WFEFK9NP32VKYVNFXPYRWQ1N1HDKV5J0DFEKHBPJCYSWCBJDRNWD7G8BN8PT97FA9AMV75MYEK4X54D1HGJ207JSVJBGFCATSPNTEYNHEQF1F220W00TBZR1HNPDQFD56FG0DJQ9KGHM8EC33H6AY9YN9CNX5R3Z4TZ4Q23W47SBHB13H6W74FQJG1F50X38VRSC4SR8RWBAFB7S4K8D2H4NMRFSQT892A3T0BTBW7HM5C0H2CK6FRKG31F7W9WP1S29013K5CXYE55CT8TH6N8J9B780R42Y5S3ZB6J6E9H76XBPSGH4TGYSR2VZRB98J417KCQMZKX1BB67E7W5KVE37TC9SJ904002",
       },
@@ -83,7 +83,7 @@ test("withdrawal selection bug repro", (t) => {
     },
     {
       denomPub: {
-        cipher: 1,
+        cipher: DenomKeyType.Rsa,
         rsa_public_key:
           
"040000Y63CF78QFPKRY77BRK9P557Q1GQWX3NCZ3HSYSK0Z7TT0KGRA7N4SKBKEHSTVHX1Z9DNXMJR4EXSY1TXCKV0GJ3T3YYC6Z0JNMJFVYQAV4FX5J90NZH1N33MZTV8HS9SMNAA9S6K73G4P99GYBB01B0P6M1KXZ5JRDR7VWBR3MEJHHGJ6QBMCJR3NWJRE3WJW9PRY8QPQ2S7KFWTWRESH2DBXCXWBD2SRN6P9YX8GRAEMFEGXC9V5GVJTEMH6ZDGNXFPWZE3JVJ2Q4N9GDYKBCHZCJ7M7M2RJ9ZV4Y64NAN9BT6XDC68215GKKRHTW1BBF1MYY6AR3JCTT9HYAM923RMVQR3TAEB7SDX8J76XRZWYH3AGJCZAQGMN5C8SSH9AHQ9RNQJQ15CN45R37X4YNFJV904002",
       },
@@ -138,7 +138,7 @@ test("withdrawal selection bug repro", (t) => {
     },
     {
       denomPub: {
-        cipher: 1,
+        cipher: DenomKeyType.Rsa,
         rsa_public_key:
           
"040000YDESWC2B962DA4WK356SC50MA3N9KV0ZSGY3RC48JCTY258W909C7EEMT5BTC5KZ5T4CERCZ141P9QF87EK2BD1XEEM5GB07MB3H19WE4CQGAS8X84JBWN83PQGQXVMWE5HFA992KMGHC566GT9ZS2QPHZB6X89C4A80Z663PYAAPXP728VHAKATGNNBQ01ZZ2XD1CH9Y38YZBSPJ4K7GB2J76GBCYAVD9ENHDVWXJAXYRPBX4KSS5TXRR3K5NEN9ZV3AJD2V65K7ABRZDF5D5V1FJZZMNJ5XZ4FEREEKEBV9TDFPGJTKDEHEC60K3DN24DAATRESDJ1ZYYSYSRCAT4BT2B62ARGVMJTT5N2R126DRW9TGRWCW0ZAF2N2WET1H4NJEW77X0QT46Z5R3MZ0XPHD04002",
       },
@@ -192,7 +192,7 @@ test("withdrawal selection bug repro", (t) => {
     },
     {
       denomPub: {
-        cipher: 1,
+        cipher: DenomKeyType.Rsa,
         rsa_public_key:
           
"040000YG3T1ADB8DVA6BD3EPV6ZHSHTDW35DEN4VH1AE6CSB7P1PSDTNTJG866PHF6QB1CCWYCVRGA0FVBJ9Q0G7KV7AD9010GDYBQH0NNPHW744MTNXVXWBGGGRGQGYK4DTYN1DSWQ1FZNDSZZPB5BEKG2PDJ93NX2JTN06Y8QMS2G734Z9XHC10EENBG2KVB7EJ3CM8PV1T32RC7AY62F3496E8D8KRHJQQTT67DSGMNKK86QXVDTYW677FG27DP20E8XY3M6FQD53NDJ1WWES91401MV1A3VXVPGC76GZVDD62W3WTJ1YMKHTTA3MRXX3VEAAH3XTKDN1ER7X6CZPMYTF8VK735VP2B2TZGTF28TTW4FZS32SBS64APCDF6SZQ427N5538TJC7SRE71YSP5ET8GS904002",
       },
@@ -247,7 +247,7 @@ test("withdrawal selection bug repro", (t) => {
     },
     {
       denomPub: {
-        cipher: 1,
+        cipher: DenomKeyType.Rsa,
         rsa_public_key:
           
"040000ZC0G60E9QQ5PD81TSDWD9GV5Y6P8Z05NSPA696DP07NGQQVSRQXBA76Q6PRB0YFX295RG4MTQJXAZZ860ET307HSC2X37XAVGQXRVB8Q4F1V7NP5ZEVKTX75DZK1QRAVHEZGQYKSSH6DBCJNQF6V9WNQF3GEYVA4KCBHA7JF772KHXM9642C28Z0AS4XXXV2PABAN5C8CHYD5H7JDFNK3920W5Q69X0BS84XZ4RE2PW6HM1WZ6KGZ3MKWWWCPKQ1FSFABRBWKAB09PF563BEBXKY6M38QETPH5EDWGANHD0SC3QV0WXYVB7BNHNNQ0J5BNV56K563SYHM4E5ND260YRJSYA1GN5YSW2B1J5T1A1EBNYF2DN6JNJKWXWEQ42G5YS17ZSZ5EWDRA9QKV8EGTCNAD04002",
       },
@@ -301,7 +301,7 @@ test("withdrawal selection bug repro", (t) => {
     },
     {
       denomPub: {
-        cipher: 1,
+        cipher: DenomKeyType.Rsa,
         rsa_public_key:
           
"040000ZSK2PMVY6E3NBQ52KXMW029M60F4BWYTDS0FZSD0PE53CNZ9H6TM3GQK1WRTEKQ5GRWJ1J9DY6Y42SP47QVT1XD1G0W05SQ5F3F7P5KSWR0FJBJ9NZBXQEVN8Q4JRC94X3JJ3XV3KBYTZ2HTDFV28C3H2SRR0XGNZB4FY85NDZF1G4AEYJJ9QB3C0V8H70YB8RV3FKTNH7XS4K4HFNZHJ5H9VMX5SM9Z2DX37HA5WFH0E2MJBVVF2BWWA5M0HPPSB365RAE2AMD42Q65A96WD80X27SB2ZNQZ8WX0K13FWF85GZ6YNYAJGE1KGN06JDEKE9QD68Z651D7XE8V6664TVVC8M68S7WD0DSXMJQKQ0BNJXNDE29Q7MRX6DA3RW0PZ44B3TKRK0294FPVZTNSTA6XF04002",
       },
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index 79220089..731e9b3a 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -43,6 +43,7 @@ import {
   DenomKeyType,
   LibtoolVersion,
   UnblindedSignature,
+  ExchangeWithdrawRequest,
 } from "@gnu-taler/taler-util";
 import {
   CoinRecord,
@@ -497,9 +498,8 @@ async function processPlanchetExchangeRequest(
         `processing planchet #${coinIdx} in withdrawal 
${withdrawalGroup.withdrawalGroupId}`,
       );
 
-      const reqBody: any = {
+      const reqBody: ExchangeWithdrawRequest = {
         denom_pub_hash: planchet.denomPubHash,
-        reserve_pub: planchet.reservePub,
         reserve_sig: planchet.withdrawSig,
         coin_ev: planchet.coinEv,
       };
@@ -580,28 +580,12 @@ async function processPlanchetVerifyAndStoreCoin(
   const { planchet, exchangeBaseUrl } = d;
 
   const planchetDenomPub = planchet.denomPub;
-  if (
-    planchetDenomPub.cipher !== DenomKeyType.Rsa &&
-    planchetDenomPub.cipher !== DenomKeyType.LegacyRsa
-  ) {
+  if (planchetDenomPub.cipher !== DenomKeyType.Rsa) {
     throw Error(`cipher (${planchetDenomPub.cipher}) not supported`);
   }
 
   let evSig = resp.ev_sig;
-  if (typeof resp.ev_sig === "string") {
-    evSig = {
-      cipher: DenomKeyType.LegacyRsa,
-      blinded_rsa_signature: resp.ev_sig,
-    };
-  } else {
-    evSig = resp.ev_sig;
-  }
-  if (
-    !(
-      evSig.cipher === DenomKeyType.Rsa ||
-      evSig.cipher === DenomKeyType.LegacyRsa
-    )
-  ) {
+  if (!(evSig.cipher === DenomKeyType.Rsa)) {
     throw Error("unsupported cipher");
   }
 
@@ -639,10 +623,7 @@ async function processPlanchetVerifyAndStoreCoin(
   }
 
   let denomSig: UnblindedSignature;
-  if (
-    planchet.denomPub.cipher === DenomKeyType.LegacyRsa ||
-    planchet.denomPub.cipher === DenomKeyType.Rsa
-  ) {
+  if (planchet.denomPub.cipher === DenomKeyType.Rsa) {
     denomSig = {
       cipher: planchet.denomPub.cipher,
       rsa_signature: denomSigRsa,
diff --git a/packages/taler-wallet-core/src/util/coinSelection.test.ts 
b/packages/taler-wallet-core/src/util/coinSelection.test.ts
index 49f8d163..1675a9a3 100644
--- a/packages/taler-wallet-core/src/util/coinSelection.test.ts
+++ b/packages/taler-wallet-core/src/util/coinSelection.test.ts
@@ -18,7 +18,7 @@
  * Imports.
  */
 import test from "ava";
-import { AmountJson, Amounts } from "@gnu-taler/taler-util";
+import { AmountJson, Amounts, DenomKeyType } from "@gnu-taler/taler-util";
 import { AvailableCoinInfo, selectPayCoins } from "./coinSelection.js";
 
 function a(x: string): AmountJson {
@@ -34,7 +34,7 @@ function fakeAci(current: string, feeDeposit: string): 
AvailableCoinInfo {
     availableAmount: a(current),
     coinPub: "foobar",
     denomPub: {
-      cipher: 1,
+      cipher: DenomKeyType.Rsa,
       rsa_public_key: "foobar",
     },
     feeDeposit: a(feeDeposit),
@@ -47,7 +47,7 @@ test("it should be able to pay if merchant takes the fees", 
(t) => {
     fakeAci("EUR:1.0", "EUR:0.1"),
     fakeAci("EUR:1.0", "EUR:0.0"),
   ];
-  acis.forEach((x, i) => x.coinPub = String(i));
+  acis.forEach((x, i) => (x.coinPub = String(i)));
 
   const res = selectPayCoins({
     candidates: {
@@ -75,7 +75,7 @@ test("it should take the last two coins if it pays less 
fees", (t) => {
     // Merchant covers the fee, this one shouldn't be used
     fakeAci("EUR:1.0", "EUR:0.0"),
   ];
-  acis.forEach((x, i) => x.coinPub = String(i));
+  acis.forEach((x, i) => (x.coinPub = String(i)));
 
   const res = selectPayCoins({
     candidates: {
@@ -102,8 +102,8 @@ test("it should take the last coins if the merchant doest 
not take all the fee",
     fakeAci("EUR:1.0", "EUR:0.5"),
     // this coin should be selected instead of previous one with fee
     fakeAci("EUR:1.0", "EUR:0.0"),
-  ]
-  acis.forEach((x, i) => x.coinPub = String(i));
+  ];
+  acis.forEach((x, i) => (x.coinPub = String(i)));
 
   const res = selectPayCoins({
     candidates: {
@@ -221,7 +221,7 @@ test("it should use the coins that spent less relative 
fee", (t) => {
     fakeAci("EUR:0.05", "EUR:0.05"),
     fakeAci("EUR:0.05", "EUR:0.05"),
   ];
-  acis.forEach((x, i) => x.coinPub = String(i));
+  acis.forEach((x, i) => (x.coinPub = String(i)));
 
   const res = selectPayCoins({
     candidates: {

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]