gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: wallet: db-less benchmarking


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet: db-less benchmarking
Date: Tue, 15 Mar 2022 17:51:15 +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 c0be2422 wallet: db-less benchmarking
c0be2422 is described below

commit c0be242292a770c4dbe6d5ed86343014d14e9a33
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue Mar 15 17:51:05 2022 +0100

    wallet: db-less benchmarking
---
 packages/taler-util/src/walletTypes.ts             |  10 +
 packages/taler-wallet-cli/src/bench2.ts            | 105 ++++++--
 packages/taler-wallet-cli/src/index.ts             |  25 +-
 .../src/integrationtests/test-wallet-dbless.ts     | 274 +--------------------
 packages/taler-wallet-core/src/bank-api-client.ts  |   5 +
 .../taler-wallet-core/src/crypto/cryptoTypes.ts    |   1 +
 .../src/crypto/workers/cryptoApi.ts                |   5 +-
 .../src/crypto/workers/cryptoImplementation.ts     |   3 +-
 .../src/dbless.ts}                                 | 209 ++++++++--------
 packages/taler-wallet-core/src/index.ts            |   3 +
 .../taler-wallet-core/src/operations/refresh.ts    | 156 ++++++------
 11 files changed, 333 insertions(+), 463 deletions(-)

diff --git a/packages/taler-util/src/walletTypes.ts 
b/packages/taler-util/src/walletTypes.ts
index 1c203797..9a3f1f8f 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -458,6 +458,16 @@ export interface TalerErrorDetails {
   details: unknown;
 }
 
+/**
+ * Minimal information needed about a planchet for unblinding a signature.
+ * 
+ * Can be a withdrawal/tipping/refresh planchet.
+ */
+export interface PlanchetUnblindInfo {
+  denomPub: DenominationPubKey;
+  blindingKey: string;
+}
+
 export interface WithdrawalPlanchet {
   coinPub: string;
   coinPriv: string;
diff --git a/packages/taler-wallet-cli/src/bench2.ts 
b/packages/taler-wallet-cli/src/bench2.ts
index 88470820..43c28882 100644
--- a/packages/taler-wallet-cli/src/bench2.ts
+++ b/packages/taler-wallet-cli/src/bench2.ts
@@ -22,16 +22,20 @@ import {
   codecForNumber,
   codecForString,
   codecOptional,
-  j2s,
   Logger,
 } from "@gnu-taler/taler-util";
 import {
-  getDefaultNodeWallet2,
-  NodeHttpLib,
-  WalletApiOperation,
-  Wallet,
-  AccessStats,
+  checkReserve,
+  createFakebankReserve,
+  CryptoApi,
+  depositCoin,
   downloadExchangeInfo,
+  findDenomOrThrow,
+  generateReserveKeypair,
+  NodeHttpLib,
+  refreshCoin,
+  SynchronousCryptoWorkerFactory,
+  withdrawCoin,
 } from "@gnu-taler/taler-wallet-core";
 
 /**
@@ -44,15 +48,79 @@ export async function runBench2(configJson: any): 
Promise<void> {
   const logger = new Logger("Bench1");
 
   // Validate the configuration file for this benchmark.
-  const benchConf = codecForBench1Config().decode(configJson);
+  const benchConf = codecForBench2Config().decode(configJson);
+  const curr = benchConf.currency;
+  const cryptoApi = new CryptoApi(new SynchronousCryptoWorkerFactory());
+
+  const http = new NodeHttpLib();
+  http.setThrottling(false);
+
+  const numIter = benchConf.iterations ?? 1;
+  const numDeposits = benchConf.deposits ?? 5;
+
+  const reserveAmount = (numDeposits + 1) * 10;
+
+  for (let i = 0; i < numIter; i++) {
+    const exchangeInfo = await downloadExchangeInfo(benchConf.exchange, http);
+
+    const reserveKeyPair = generateReserveKeypair();
+
+    console.log("creating fakebank reserve");
+
+    await createFakebankReserve({
+      amount: `${curr}:${reserveAmount}`,
+      exchangeInfo,
+      fakebankBaseUrl: benchConf.bank,
+      http,
+      reservePub: reserveKeyPair.reservePub,
+    });
+
+    console.log("waiting for reserve");
+
+    await checkReserve(http, benchConf.exchange, reserveKeyPair.reservePub);
 
-  const myHttpLib = new NodeHttpLib();
-  myHttpLib.setThrottling(false);
+    console.log("reserve found");
 
-  const exchangeInfo = await downloadExchangeInfo(
-    benchConf.exchange,
-    myHttpLib,
-  );
+    const d1 = findDenomOrThrow(exchangeInfo, `${curr}:8`);
+
+    for (let j = 0; j < numDeposits; j++) {
+      console.log("withdrawing coin");
+      const coin = await withdrawCoin({
+        http,
+        cryptoApi,
+        reserveKeyPair,
+        denom: d1,
+        exchangeBaseUrl: benchConf.exchange,
+      });
+
+      console.log("depositing coin");
+
+      await depositCoin({
+        amount: `${curr}:4`,
+        coin: coin,
+        cryptoApi,
+        exchangeBaseUrl: benchConf.exchange,
+        http,
+        depositPayto: benchConf.payto,
+      });
+
+      const refreshDenoms = [
+        findDenomOrThrow(exchangeInfo, `${curr}:1`),
+        findDenomOrThrow(exchangeInfo, `${curr}:1`),
+      ];
+
+      console.log("refreshing coin");
+
+      await refreshCoin({
+        oldCoin: coin,
+        cryptoApi,
+        http,
+        newDenoms: refreshDenoms,
+      });
+
+      console.log("refresh done");
+    }
+  }
 }
 
 /**
@@ -83,18 +151,12 @@ interface Bench2Config {
   currency: string;
 
   deposits?: number;
-
-  /**
-   * How any iterations run until the wallet db gets purged
-   * Defaults to 20.
-   */
-  restartAfter?: number;
 }
 
 /**
  * Schema validation codec for Bench1Config.
  */
-const codecForBench1Config = () =>
+const codecForBench2Config = () =>
   buildCodecForObject<Bench2Config>()
     .property("bank", codecForString())
     .property("payto", codecForString())
@@ -102,5 +164,4 @@ const codecForBench1Config = () =>
     .property("iterations", codecOptional(codecForNumber()))
     .property("deposits", codecOptional(codecForNumber()))
     .property("currency", codecForString())
-    .property("restartAfter", codecOptional(codecForNumber()))
-    .build("Bench1Config");
+    .build("Bench2Config");
diff --git a/packages/taler-wallet-cli/src/index.ts 
b/packages/taler-wallet-cli/src/index.ts
index 3b72f74b..f754ca91 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -62,6 +62,7 @@ import { lintExchangeDeployment } from "./lint.js";
 import { runBench1 } from "./bench1.js";
 import { runEnv1 } from "./env1.js";
 import { GlobalTestState, runTestWithState } from "./harness/harness.js";
+import { runBench2 } from "./bench2.js";
 
 // This module also serves as the entry point for the crypto
 // thread worker, and thus must expose these two handlers.
@@ -168,8 +169,7 @@ export const walletCli = clk
     },
   })
   .maybeOption("inhibit", ["--inhibit"], clk.STRING, {
-    help:
-      "Inhibit running certain operations, useful for debugging and testing.",
+    help: "Inhibit running certain operations, useful for debugging and 
testing.",
   })
   .flag("noThrottle", ["--no-throttle"], {
     help: "Don't do any request throttling.",
@@ -559,8 +559,7 @@ backupCli.subcommand("status", "status").action(async 
(args) => {
 backupCli
   .subcommand("recoveryLoad", "load-recovery")
   .maybeOption("strategy", ["--strategy"], clk.STRING, {
-    help:
-      "Strategy for resolving a conflict with the existing wallet key 
('theirs' or 'ours')",
+    help: "Strategy for resolving a conflict with the existing wallet key 
('theirs' or 'ours')",
   })
   .action(async (args) => {
     await withWallet(args, async (wallet) => {
@@ -636,8 +635,7 @@ depositCli
   });
 
 const advancedCli = walletCli.subcommand("advancedArgs", "advanced", {
-  help:
-    "Subcommands for advanced operations (only use if you know what you're 
doing!).",
+  help: "Subcommands for advanced operations (only use if you know what you're 
doing!).",
 });
 
 advancedCli
@@ -655,6 +653,21 @@ advancedCli
     await runBench1(config);
   });
 
+advancedCli
+  .subcommand("bench2", "bench2", {
+    help: "Run the 'bench2' benchmark",
+  })
+  .requiredOption("configJson", ["--config-json"], clk.STRING)
+  .action(async (args) => {
+    let config: any;
+    try {
+      config = JSON.parse(args.bench2.configJson);
+    } catch (e) {
+      console.log("Could not parse config JSON");
+    }
+    await runBench2(config);
+  });
+
 advancedCli
   .subcommand("env1", "env1", {
     help: "Run a test environment for bench1",
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts 
b/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
index 9ff605df..93c22af7 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
@@ -17,277 +17,24 @@
 /**
  * Imports.
  */
+import { j2s } from "@gnu-taler/taler-util";
 import {
-  AmountJson,
-  AmountLike,
-  Amounts,
-  AmountString,
-  codecForBankWithdrawalOperationPostResponse,
-  codecForDepositSuccess,
-  codecForExchangeMeltResponse,
-  codecForWithdrawResponse,
-  DenominationPubKey,
-  eddsaGetPublic,
-  encodeCrock,
-  ExchangeMeltRequest,
-  ExchangeProtocolVersion,
-  ExchangeWithdrawRequest,
-  getRandomBytes,
-  getTimestampNow,
-  hashWire,
-  j2s,
-  Timestamp,
-  UnblindedSignature,
-} from "@gnu-taler/taler-util";
-import {
-  BankAccessApi,
-  BankApi,
-  BankServiceHandle,
+  checkReserve,
   CryptoApi,
-  DenominationRecord,
+  depositCoin,
   downloadExchangeInfo,
-  ExchangeInfo,
-  getBankWithdrawalInfo,
-  HttpRequestLibrary,
-  isWithdrawableDenom,
+  findDenomOrThrow,
+  generateReserveKeypair,
   NodeHttpLib,
   OperationFailedError,
-  readSuccessResponseJsonOrThrow,
+  refreshCoin,
   SynchronousCryptoWorkerFactory,
+  topupReserveWithDemobank,
+  withdrawCoin,
 } from "@gnu-taler/taler-wallet-core";
 import { GlobalTestState } from "../harness/harness.js";
 import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
 
-const httpLib = new NodeHttpLib();
-
-export interface ReserveKeypair {
-  reservePub: string;
-  reservePriv: string;
-}
-
-/**
- * Denormalized info about a coin.
- */
-export interface CoinInfo {
-  coinPub: string;
-  coinPriv: string;
-  exchangeBaseUrl: string;
-  denomSig: UnblindedSignature;
-  denomPub: DenominationPubKey;
-  denomPubHash: string;
-  feeDeposit: string;
-  feeRefresh: string;
-}
-
-export function generateReserveKeypair(): ReserveKeypair {
-  const priv = getRandomBytes(32);
-  const pub = eddsaGetPublic(priv);
-  return {
-    reservePriv: encodeCrock(priv),
-    reservePub: encodeCrock(pub),
-  };
-}
-
-async function topupReserveWithDemobank(
-  reservePub: string,
-  bankBaseUrl: string,
-  exchangeInfo: ExchangeInfo,
-  amount: AmountString,
-) {
-  const bankHandle: BankServiceHandle = {
-    baseUrl: bankBaseUrl,
-    http: httpLib,
-  };
-  const bankUser = await BankApi.createRandomBankUser(bankHandle);
-  const wopi = await BankAccessApi.createWithdrawalOperation(
-    bankHandle,
-    bankUser,
-    amount,
-  );
-  const bankInfo = await getBankWithdrawalInfo(
-    httpLib,
-    wopi.taler_withdraw_uri,
-  );
-  const bankStatusUrl = bankInfo.extractedStatusUrl;
-  if (!bankInfo.suggestedExchange) {
-    throw Error("no suggested exchange");
-  }
-  const plainPaytoUris =
-    exchangeInfo.wire.accounts.map((x) => x.payto_uri) ?? [];
-  if (plainPaytoUris.length <= 0) {
-    throw new Error();
-  }
-  const httpResp = await httpLib.postJson(bankStatusUrl, {
-    reserve_pub: reservePub,
-    selected_exchange: plainPaytoUris[0],
-  });
-  await readSuccessResponseJsonOrThrow(
-    httpResp,
-    codecForBankWithdrawalOperationPostResponse(),
-  );
-  await BankApi.confirmWithdrawalOperation(bankHandle, bankUser, wopi);
-}
-
-async function withdrawCoin(args: {
-  http: HttpRequestLibrary;
-  cryptoApi: CryptoApi;
-  reserveKeyPair: ReserveKeypair;
-  denom: DenominationRecord;
-  exchangeBaseUrl: string;
-}): Promise<CoinInfo> {
-  const { http, cryptoApi, reserveKeyPair, denom, exchangeBaseUrl } = args;
-  const planchet = await cryptoApi.createPlanchet({
-    coinIndex: 0,
-    denomPub: denom.denomPub,
-    feeWithdraw: denom.feeWithdraw,
-    reservePriv: reserveKeyPair.reservePriv,
-    reservePub: reserveKeyPair.reservePub,
-    secretSeed: encodeCrock(getRandomBytes(32)),
-    value: denom.value,
-  });
-
-  const reqBody: ExchangeWithdrawRequest = {
-    denom_pub_hash: planchet.denomPubHash,
-    reserve_sig: planchet.withdrawSig,
-    coin_ev: planchet.coinEv,
-  };
-  const reqUrl = new URL(
-    `reserves/${planchet.reservePub}/withdraw`,
-    exchangeBaseUrl,
-  ).href;
-
-  const resp = await http.postJson(reqUrl, reqBody);
-  const r = await readSuccessResponseJsonOrThrow(
-    resp,
-    codecForWithdrawResponse(),
-  );
-
-  const ubSig = await cryptoApi.unblindDenominationSignature({
-    planchet,
-    evSig: r.ev_sig,
-  });
-
-  return {
-    coinPriv: planchet.coinPriv,
-    coinPub: planchet.coinPub,
-    denomSig: ubSig,
-    denomPub: denom.denomPub,
-    denomPubHash: denom.denomPubHash,
-    feeDeposit: Amounts.stringify(denom.feeDeposit),
-    feeRefresh: Amounts.stringify(denom.feeRefresh),
-    exchangeBaseUrl: args.exchangeBaseUrl,
-  };
-}
-
-function findDenomOrThrow(
-  exchangeInfo: ExchangeInfo,
-  amount: AmountString,
-): DenominationRecord {
-  for (const d of exchangeInfo.keys.currentDenominations) {
-    if (Amounts.cmp(d.value, amount) === 0 && isWithdrawableDenom(d)) {
-      return d;
-    }
-  }
-  throw new Error("no matching denomination found");
-}
-
-async function depositCoin(args: {
-  http: HttpRequestLibrary;
-  cryptoApi: CryptoApi;
-  exchangeBaseUrl: string;
-  coin: CoinInfo;
-  amount: AmountString;
-}) {
-  const { coin, http, cryptoApi } = args;
-  const depositPayto = "payto://x-taler-bank/localhost/foo";
-  const wireSalt = encodeCrock(getRandomBytes(16));
-  const contractTermsHash = encodeCrock(getRandomBytes(64));
-  const depositTimestamp = getTimestampNow();
-  const refundDeadline = getTimestampNow();
-  const merchantPub = encodeCrock(getRandomBytes(32));
-  const dp = await cryptoApi.signDepositPermission({
-    coinPriv: coin.coinPriv,
-    coinPub: coin.coinPub,
-    contractTermsHash,
-    denomKeyType: coin.denomPub.cipher,
-    denomPubHash: coin.denomPubHash,
-    denomSig: coin.denomSig,
-    exchangeBaseUrl: args.exchangeBaseUrl,
-    feeDeposit: Amounts.parseOrThrow(coin.feeDeposit),
-    merchantPub,
-    spendAmount: Amounts.parseOrThrow(args.amount),
-    timestamp: depositTimestamp,
-    refundDeadline: refundDeadline,
-    wireInfoHash: hashWire(depositPayto, wireSalt),
-  });
-  const requestBody = {
-    contribution: Amounts.stringify(dp.contribution),
-    merchant_payto_uri: depositPayto,
-    wire_salt: wireSalt,
-    h_contract_terms: contractTermsHash,
-    ub_sig: coin.denomSig,
-    timestamp: depositTimestamp,
-    wire_transfer_deadline: getTimestampNow(),
-    refund_deadline: refundDeadline,
-    coin_sig: dp.coin_sig,
-    denom_pub_hash: dp.h_denom,
-    merchant_pub: merchantPub,
-  };
-  const url = new URL(`coins/${dp.coin_pub}/deposit`, dp.exchange_url);
-  const httpResp = await http.postJson(url.href, requestBody);
-  await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());
-}
-
-async function refreshCoin(req: {
-  http: HttpRequestLibrary;
-  cryptoApi: CryptoApi;
-  oldCoin: CoinInfo;
-  newDenoms: DenominationRecord[];
-}): Promise<void> {
-  const { cryptoApi, oldCoin, http } = req;
-  const refreshSessionSeed = encodeCrock(getRandomBytes(32));
-  const session = await cryptoApi.deriveRefreshSession({
-    exchangeProtocolVersion: ExchangeProtocolVersion.V12,
-    feeRefresh: Amounts.parseOrThrow(oldCoin.feeRefresh),
-    kappa: 3,
-    meltCoinDenomPubHash: oldCoin.denomPubHash,
-    meltCoinPriv: oldCoin.coinPriv,
-    meltCoinPub: oldCoin.coinPub,
-    sessionSecretSeed: refreshSessionSeed,
-    newCoinDenoms: req.newDenoms.map((x) => ({
-      count: 1,
-      denomPub: x.denomPub,
-      feeWithdraw: x.feeWithdraw,
-      value: x.value,
-    })),
-  });
-
-  const meltReqBody: ExchangeMeltRequest = {
-    coin_pub: oldCoin.coinPub,
-    confirm_sig: session.confirmSig,
-    denom_pub_hash: oldCoin.denomPubHash,
-    denom_sig: oldCoin.denomSig,
-    rc: session.hash,
-    value_with_fee: Amounts.stringify(session.meltValueWithFee),
-  };
-
-  const reqUrl = new URL(
-    `coins/${oldCoin.coinPub}/melt`,
-    oldCoin.exchangeBaseUrl,
-  );
-
-  const resp = await http.postJson(reqUrl.href, meltReqBody);
-
-  const meltResponse = await readSuccessResponseJsonOrThrow(
-    resp,
-    codecForExchangeMeltResponse(),
-  );
-
-  const norevealIndex = meltResponse.noreveal_index;
-
-  
-}
-
 /**
  * Run test for basic, bank-integrated withdrawal and payment.
  */
@@ -307,6 +54,7 @@ export async function runWalletDblessTest(t: 
GlobalTestState) {
     const reserveKeyPair = generateReserveKeypair();
 
     await topupReserveWithDemobank(
+      http,
       reserveKeyPair.reservePub,
       bank.baseUrl,
       exchangeInfo,
@@ -315,6 +63,8 @@ export async function runWalletDblessTest(t: 
GlobalTestState) {
 
     await exchange.runWirewatchOnce();
 
+    await checkReserve(http, exchange.baseUrl, reserveKeyPair.reservePub);
+
     const d1 = findDenomOrThrow(exchangeInfo, "TESTKUDOS:8");
 
     const coin = await withdrawCoin({
@@ -338,7 +88,7 @@ export async function runWalletDblessTest(t: 
GlobalTestState) {
       findDenomOrThrow(exchangeInfo, "TESTKUDOS:1"),
     ];
 
-    const freshCoins = await refreshCoin({
+    await refreshCoin({
       oldCoin: coin,
       cryptoApi,
       http,
diff --git a/packages/taler-wallet-core/src/bank-api-client.ts 
b/packages/taler-wallet-core/src/bank-api-client.ts
index 744c3b83..a61ea2ee 100644
--- a/packages/taler-wallet-core/src/bank-api-client.ts
+++ b/packages/taler-wallet-core/src/bank-api-client.ts
@@ -28,6 +28,8 @@ import {
   codecForString,
   encodeCrock,
   getRandomBytes,
+  j2s,
+  Logger,
 } from "@gnu-taler/taler-util";
 import {
   HttpRequestLibrary,
@@ -35,6 +37,8 @@ import {
   readSuccessResponseJsonOrThrow,
 } from "./index.browser.js";
 
+const logger = new Logger("bank-api-client.ts");
+
 export enum CreditDebitIndicator {
   Credit = "credit",
   Debit = "debit",
@@ -98,6 +102,7 @@ export namespace BankApi {
     const resp = await bank.http.postJson(url.href, { username, password });
     let paytoUri = `payto://x-taler-bank/localhost/${username}`;
     if (resp.status !== 200 && resp.status !== 202) {
+      logger.error(`${j2s(await resp.json())}`)
       throw new Error();
     }
     try {
diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts 
b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
index 00a7fba8..3b339604 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
@@ -42,6 +42,7 @@ export interface RefreshNewDenomInfo {
   value: AmountJson;
   feeWithdraw: AmountJson;
   denomPub: DenominationPubKey;
+  denomPubHash: string;
 }
 
 /**
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts 
b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
index b5a5950b..82039734 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
@@ -30,6 +30,7 @@ import {
   BlindedDenominationSignature,
   CoinDepositPermission,
   CoinEnvelope,
+  PlanchetUnblindInfo,
   RecoupRefreshRequest,
   RecoupRequest,
   UnblindedSignature,
@@ -206,7 +207,7 @@ export class CryptoApi {
       }
     };
     ws.terminationTimerHandle = timer.after(15 * 1000, destroy);
-    //ws.terminationTimerHandle.unref();
+    ws.terminationTimerHandle.unref();
   }
 
   handleWorkerError(ws: WorkerState, e: any): void {
@@ -331,7 +332,7 @@ export class CryptoApi {
   }
 
   unblindDenominationSignature(req: {
-    planchet: WithdrawalPlanchet;
+    planchet: PlanchetUnblindInfo;
     evSig: BlindedDenominationSignature;
   }): Promise<UnblindedSignature> {
     return this.doRpc<UnblindedSignature>(
diff --git 
a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts 
b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index 15a086ae..b51d499d 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -73,6 +73,7 @@ import {
   BlindedDenominationSignature,
   RsaUnblindedSignature,
   UnblindedSignature,
+  PlanchetUnblindInfo,
 } from "@gnu-taler/taler-util";
 import bigint from "big-integer";
 import { DenominationRecord, WireFee } from "../../db.js";
@@ -432,7 +433,7 @@ export class CryptoImplementation {
   }
 
   unblindDenominationSignature(req: {
-    planchet: WithdrawalPlanchet;
+    planchet: PlanchetUnblindInfo;
     evSig: BlindedDenominationSignature;
   }): UnblindedSignature {
     if (req.evSig.cipher === DenomKeyType.Rsa) {
diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts 
b/packages/taler-wallet-core/src/dbless.ts
similarity index 68%
copy from packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
copy to packages/taler-wallet-core/src/dbless.ts
index 9ff605df..85a72e28 100644
--- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-dbless.ts
+++ b/packages/taler-wallet-core/src/dbless.ts
@@ -1,6 +1,6 @@
 /*
  This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
+ (C) 2021 Taler Systems S.A.
 
  GNU Taler is free software; you can redistribute it and/or modify it under the
  terms of the GNU General Public License as published by the Free Software
@@ -14,17 +14,25 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+/**
+ * Helper functions to run wallet functionality (withdrawal, deposit, refresh)
+ * without a database or retry loop.
+ *
+ * Used for benchmarking, where we want to benchmark the exchange, but the
+ * normal wallet would be too sluggish.
+ */
+
 /**
  * Imports.
  */
 import {
-  AmountJson,
-  AmountLike,
   Amounts,
   AmountString,
+  codecForAny,
   codecForBankWithdrawalOperationPostResponse,
   codecForDepositSuccess,
   codecForExchangeMeltResponse,
+  codecForExchangeRevealResponse,
   codecForWithdrawResponse,
   DenominationPubKey,
   eddsaGetPublic,
@@ -35,30 +43,23 @@ import {
   getRandomBytes,
   getTimestampNow,
   hashWire,
-  j2s,
-  Timestamp,
+  Logger,
+  parsePaytoUri,
   UnblindedSignature,
 } from "@gnu-taler/taler-util";
+import { DenominationRecord } from "./db.js";
 import {
-  BankAccessApi,
-  BankApi,
-  BankServiceHandle,
+  assembleRefreshRevealRequest,
   CryptoApi,
-  DenominationRecord,
-  downloadExchangeInfo,
   ExchangeInfo,
   getBankWithdrawalInfo,
   HttpRequestLibrary,
   isWithdrawableDenom,
-  NodeHttpLib,
-  OperationFailedError,
   readSuccessResponseJsonOrThrow,
-  SynchronousCryptoWorkerFactory,
-} from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
+} from "./index.browser.js";
+import { BankAccessApi, BankApi, BankServiceHandle } from "./index.js";
 
-const httpLib = new NodeHttpLib();
+const logger = new Logger("dbless.ts");
 
 export interface ReserveKeypair {
   reservePub: string;
@@ -88,7 +89,28 @@ export function generateReserveKeypair(): ReserveKeypair {
   };
 }
 
-async function topupReserveWithDemobank(
+/**
+ * Check the status of a reserve, use long-polling to wait
+ * until the reserve actually has been created.
+ */
+export async function checkReserve(
+  http: HttpRequestLibrary,
+  exchangeBaseUrl: string,
+  reservePub: string,
+  longpollTimeoutMs: number = 500,
+): Promise<void> {
+  const reqUrl = new URL(`reserves/${reservePub}`, exchangeBaseUrl);
+  if (longpollTimeoutMs) {
+    reqUrl.searchParams.set("timeout_ms", `${longpollTimeoutMs}`);
+  }
+  const resp = await http.get(reqUrl.href);
+  if (resp.status !== 200) {
+    throw new Error("reserve not okay");
+  }
+}
+
+export async function topupReserveWithDemobank(
+  http: HttpRequestLibrary,
   reservePub: string,
   bankBaseUrl: string,
   exchangeInfo: ExchangeInfo,
@@ -96,7 +118,7 @@ async function topupReserveWithDemobank(
 ) {
   const bankHandle: BankServiceHandle = {
     baseUrl: bankBaseUrl,
-    http: httpLib,
+    http,
   };
   const bankUser = await BankApi.createRandomBankUser(bankHandle);
   const wopi = await BankAccessApi.createWithdrawalOperation(
@@ -104,10 +126,7 @@ async function topupReserveWithDemobank(
     bankUser,
     amount,
   );
-  const bankInfo = await getBankWithdrawalInfo(
-    httpLib,
-    wopi.taler_withdraw_uri,
-  );
+  const bankInfo = await getBankWithdrawalInfo(http, wopi.taler_withdraw_uri);
   const bankStatusUrl = bankInfo.extractedStatusUrl;
   if (!bankInfo.suggestedExchange) {
     throw Error("no suggested exchange");
@@ -117,7 +136,7 @@ async function topupReserveWithDemobank(
   if (plainPaytoUris.length <= 0) {
     throw new Error();
   }
-  const httpResp = await httpLib.postJson(bankStatusUrl, {
+  const httpResp = await http.postJson(bankStatusUrl, {
     reserve_pub: reservePub,
     selected_exchange: plainPaytoUris[0],
   });
@@ -128,7 +147,7 @@ async function topupReserveWithDemobank(
   await BankApi.confirmWithdrawalOperation(bankHandle, bankUser, wopi);
 }
 
-async function withdrawCoin(args: {
+export async function withdrawCoin(args: {
   http: HttpRequestLibrary;
   cryptoApi: CryptoApi;
   reserveKeyPair: ReserveKeypair;
@@ -179,7 +198,7 @@ async function withdrawCoin(args: {
   };
 }
 
-function findDenomOrThrow(
+export function findDenomOrThrow(
   exchangeInfo: ExchangeInfo,
   amount: AmountString,
 ): DenominationRecord {
@@ -191,15 +210,17 @@ function findDenomOrThrow(
   throw new Error("no matching denomination found");
 }
 
-async function depositCoin(args: {
+export async function depositCoin(args: {
   http: HttpRequestLibrary;
   cryptoApi: CryptoApi;
   exchangeBaseUrl: string;
   coin: CoinInfo;
   amount: AmountString;
+  depositPayto?: string;
 }) {
   const { coin, http, cryptoApi } = args;
-  const depositPayto = "payto://x-taler-bank/localhost/foo";
+  const depositPayto =
+    args.depositPayto ?? "payto://x-taler-bank/localhost/foo";
   const wireSalt = encodeCrock(getRandomBytes(16));
   const contractTermsHash = encodeCrock(getRandomBytes(64));
   const depositTimestamp = getTimestampNow();
@@ -238,7 +259,7 @@ async function depositCoin(args: {
   await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());
 }
 
-async function refreshCoin(req: {
+export async function refreshCoin(req: {
   http: HttpRequestLibrary;
   cryptoApi: CryptoApi;
   oldCoin: CoinInfo;
@@ -257,6 +278,7 @@ async function refreshCoin(req: {
     newCoinDenoms: req.newDenoms.map((x) => ({
       count: 1,
       denomPub: x.denomPub,
+      denomPubHash: x.denomPubHash,
       feeWithdraw: x.feeWithdraw,
       value: x.value,
     })),
@@ -271,88 +293,77 @@ async function refreshCoin(req: {
     value_with_fee: Amounts.stringify(session.meltValueWithFee),
   };
 
-  const reqUrl = new URL(
+  logger.info("requesting melt");
+
+  const meltReqUrl = new URL(
     `coins/${oldCoin.coinPub}/melt`,
     oldCoin.exchangeBaseUrl,
   );
 
-  const resp = await http.postJson(reqUrl.href, meltReqBody);
+  logger.info("requesting melt done");
+
+  const meltHttpResp = await http.postJson(meltReqUrl.href, meltReqBody);
 
   const meltResponse = await readSuccessResponseJsonOrThrow(
-    resp,
+    meltHttpResp,
     codecForExchangeMeltResponse(),
   );
 
   const norevealIndex = meltResponse.noreveal_index;
 
-  
+  const revealRequest = await assembleRefreshRevealRequest({
+    cryptoApi,
+    derived: session,
+    newDenoms: req.newDenoms.map((x) => ({
+      count: 1,
+      denomPubHash: x.denomPubHash,
+    })),
+    norevealIndex,
+    oldCoinPriv: oldCoin.coinPriv,
+    oldCoinPub: oldCoin.coinPub,
+  });
+
+  logger.info("requesting reveal");
+  const reqUrl = new URL(
+    `refreshes/${session.hash}/reveal`,
+    oldCoin.exchangeBaseUrl,
+  );
+
+  const revealResp = await http.postJson(reqUrl.href, revealRequest);
+
+  logger.info("requesting reveal done");
+
+  const reveal = await readSuccessResponseJsonOrThrow(
+    revealResp,
+    codecForExchangeRevealResponse(),
+  );
+
+  // We could unblind here, but we only use this function to
+  // benchmark the exchange.
 }
 
-/**
- * Run test for basic, bank-integrated withdrawal and payment.
- */
-export async function runWalletDblessTest(t: GlobalTestState) {
-  // Set up test environment
-
-  const { bank, exchange } = await createSimpleTestkudosEnvironment(t);
-
-  const http = new NodeHttpLib();
-  const cryptoApi = new CryptoApi(new SynchronousCryptoWorkerFactory());
-
-  try {
-    // Withdraw digital cash into the wallet.
-
-    const exchangeInfo = await downloadExchangeInfo(exchange.baseUrl, http);
-
-    const reserveKeyPair = generateReserveKeypair();
-
-    await topupReserveWithDemobank(
-      reserveKeyPair.reservePub,
-      bank.baseUrl,
-      exchangeInfo,
-      "TESTKUDOS:10",
-    );
-
-    await exchange.runWirewatchOnce();
-
-    const d1 = findDenomOrThrow(exchangeInfo, "TESTKUDOS:8");
-
-    const coin = await withdrawCoin({
-      http,
-      cryptoApi,
-      reserveKeyPair,
-      denom: d1,
-      exchangeBaseUrl: exchange.baseUrl,
-    });
-
-    await depositCoin({
-      amount: "TESTKUDOS:4",
-      coin: coin,
-      cryptoApi,
-      exchangeBaseUrl: exchange.baseUrl,
-      http,
-    });
-
-    const refreshDenoms = [
-      findDenomOrThrow(exchangeInfo, "TESTKUDOS:1"),
-      findDenomOrThrow(exchangeInfo, "TESTKUDOS:1"),
-    ];
-
-    const freshCoins = await refreshCoin({
-      oldCoin: coin,
-      cryptoApi,
-      http,
-      newDenoms: refreshDenoms,
-    });
-  } catch (e) {
-    if (e instanceof OperationFailedError) {
-      console.log(e);
-      console.log(j2s(e.operationError));
-    } else {
-      console.log(e);
-    }
-    throw e;
+export async function createFakebankReserve(args: {
+  http: HttpRequestLibrary;
+  fakebankBaseUrl: string;
+  amount: string;
+  reservePub: string;
+  exchangeInfo: ExchangeInfo;
+}): Promise<void> {
+  const { http, fakebankBaseUrl, amount, reservePub } = args;
+  const paytoUri = args.exchangeInfo.wire.accounts[0].payto_uri;
+  const pt = parsePaytoUri(paytoUri);
+  if (!pt) {
+    throw Error("failed to parse payto URI");
   }
+  const components = pt.targetPath.split("/");
+  const creditorAcct = components[components.length - 1];
+  const fbReq = await http.postJson(
+    new URL(`${creditorAcct}/admin/add-incoming`, fakebankBaseUrl).href,
+    {
+      amount,
+      reserve_pub: reservePub,
+      debit_account: "payto://x-taler-bank/localhost/testdebtor",
+    },
+  );
+  const fbResp = await readSuccessResponseJsonOrThrow(fbReq, codecForAny());
 }
-
-runWalletDblessTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-core/src/index.ts 
b/packages/taler-wallet-core/src/index.ts
index cc01e914..93430732 100644
--- a/packages/taler-wallet-core/src/index.ts
+++ b/packages/taler-wallet-core/src/index.ts
@@ -54,4 +54,7 @@ export * from "./bank-api-client.js";
 
 export * from "./operations/reserves.js";
 export * from "./operations/withdraw.js";
+export * from "./operations/refresh.js";
+
+export * from "./dbless.js";
 
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index cc2a1c56..8b6d8b2e 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -15,6 +15,7 @@
  */
 
 import {
+  CoinPublicKeyString,
   DenomKeyType,
   encodeCrock,
   ExchangeMeltRequest,
@@ -79,8 +80,12 @@ import {
   isWithdrawableDenom,
   selectWithdrawalDenominations,
 } from "./withdraw.js";
-import { RefreshNewDenomInfo } from "../crypto/cryptoTypes.js";
+import {
+  DerivedRefreshSession,
+  RefreshNewDenomInfo,
+} from "../crypto/cryptoTypes.js";
 import { GetReadWriteAccess } from "../util/query.js";
+import { CryptoApi } from "../index.browser.js";
 
 const logger = new Logger("refresh.ts");
 
@@ -357,6 +362,7 @@ async function refreshMelt(
         newCoinDenoms.push({
           count: dh.count,
           denomPub: newDenom.denomPub,
+          denomPubHash: newDenom.denomPubHash,
           feeWithdraw: newDenom.feeWithdraw,
           value: newDenom.value,
         });
@@ -472,6 +478,62 @@ async function refreshMelt(
   });
 }
 
+export async function assembleRefreshRevealRequest(args: {
+  cryptoApi: CryptoApi;
+  derived: DerivedRefreshSession;
+  norevealIndex: number;
+  oldCoinPub: CoinPublicKeyString;
+  oldCoinPriv: string;
+  newDenoms: {
+    denomPubHash: string;
+    count: number;
+  }[];
+}): Promise<ExchangeRefreshRevealRequest> {
+  const {
+    derived,
+    norevealIndex,
+    cryptoApi,
+    oldCoinPriv,
+    oldCoinPub,
+    newDenoms,
+  } = args;
+  const privs = Array.from(derived.transferPrivs);
+  privs.splice(norevealIndex, 1);
+
+  const planchets = derived.planchetsForGammas[norevealIndex];
+  if (!planchets) {
+    throw Error("refresh index error");
+  }
+
+  const newDenomsFlat: string[] = [];
+  const linkSigs: string[] = [];
+
+  for (let i = 0; i < newDenoms.length; i++) {
+    const dsel = newDenoms[i];
+    for (let j = 0; j < dsel.count; j++) {
+      const newCoinIndex = linkSigs.length;
+      const linkSig = await cryptoApi.signCoinLink(
+        oldCoinPriv,
+        dsel.denomPubHash,
+        oldCoinPub,
+        derived.transferPubs[norevealIndex],
+        planchets[newCoinIndex].coinEv,
+      );
+      linkSigs.push(linkSig);
+      newDenomsFlat.push(dsel.denomPubHash);
+    }
+  }
+
+  const req: ExchangeRefreshRevealRequest = {
+    coin_evs: planchets.map((x) => x.coinEv),
+    new_denoms_h: newDenomsFlat,
+    transfer_privs: privs,
+    transfer_pub: derived.transferPubs[norevealIndex],
+    link_sigs: linkSigs,
+  };
+  return req;
+}
+
 async function refreshReveal(
   ws: InternalWalletState,
   refreshGroupId: string,
@@ -527,6 +589,7 @@ async function refreshReveal(
         newCoinDenoms.push({
           count: dh.count,
           denomPub: newDenom.denomPub,
+          denomPubHash: newDenom.denomPubHash,
           feeWithdraw: newDenom.feeWithdraw,
           value: newDenom.value,
         });
@@ -575,46 +638,20 @@ async function refreshReveal(
     sessionSecretSeed: refreshSession.sessionSecretSeed,
   });
 
-  const privs = Array.from(derived.transferPrivs);
-  privs.splice(norevealIndex, 1);
-
-  const planchets = derived.planchetsForGammas[norevealIndex];
-  if (!planchets) {
-    throw Error("refresh index error");
-  }
-
-  const newDenomsFlat: string[] = [];
-  const linkSigs: string[] = [];
-
-  for (let i = 0; i < refreshSession.newDenoms.length; i++) {
-    const dsel = refreshSession.newDenoms[i];
-    for (let j = 0; j < dsel.count; j++) {
-      const newCoinIndex = linkSigs.length;
-      const linkSig = await ws.cryptoApi.signCoinLink(
-        oldCoin.coinPriv,
-        dsel.denomPubHash,
-        oldCoin.coinPub,
-        derived.transferPubs[norevealIndex],
-        planchets[newCoinIndex].coinEv,
-      );
-      linkSigs.push(linkSig);
-      newDenomsFlat.push(dsel.denomPubHash);
-    }
-  }
-
-  const req: ExchangeRefreshRevealRequest = {
-    coin_evs: planchets.map((x) => x.coinEv),
-    new_denoms_h: newDenomsFlat,
-    transfer_privs: privs,
-    transfer_pub: derived.transferPubs[norevealIndex],
-    link_sigs: linkSigs,
-  };
-
   const reqUrl = new URL(
     `refreshes/${derived.hash}/reveal`,
     oldCoin.exchangeBaseUrl,
   );
 
+  const req = await assembleRefreshRevealRequest({
+    cryptoApi: ws.cryptoApi,
+    derived,
+    newDenoms: newCoinDenoms,
+    norevealIndex: norevealIndex,
+    oldCoinPriv: oldCoin.coinPriv,
+    oldCoinPub: oldCoin.coinPub,
+  });
+
   const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], async () => {
     return await ws.http.postJson(reqUrl.href, req, {
       timeout: getRefreshRequestTimeout(refreshGroup),
@@ -629,51 +666,28 @@ async function refreshReveal(
   const coins: CoinRecord[] = [];
 
   for (let i = 0; i < refreshSession.newDenoms.length; i++) {
+    const ncd = newCoinDenoms[i];
     for (let j = 0; j < refreshSession.newDenoms[i].count; j++) {
       const newCoinIndex = coins.length;
-      // FIXME: Look up in earlier transaction!
-      const denom = await ws.db
-        .mktx((x) => ({
-          denominations: x.denominations,
-        }))
-        .runReadOnly(async (tx) => {
-          return tx.denominations.get([
-            oldCoin.exchangeBaseUrl,
-            refreshSession.newDenoms[i].denomPubHash,
-          ]);
-        });
-      if (!denom) {
-        console.error("denom not found");
-        continue;
-      }
       const pc = derived.planchetsForGammas[norevealIndex][newCoinIndex];
-      if (denom.denomPub.cipher !== DenomKeyType.Rsa) {
+      if (ncd.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) {
-        rsaSig = evSig.blinded_rsa_signature;
-      } else {
-        throw Error("unsupported cipher");
-      }
-      const denomSigRsa = await ws.cryptoApi.rsaUnblind(
-        rsaSig,
-        pc.blindingKey,
-        denom.denomPub.rsa_public_key,
-      );
+      const denomSig = await ws.cryptoApi.unblindDenominationSignature({
+        planchet: {
+          blindingKey: pc.blindingKey,
+          denomPub: ncd.denomPub,
+        },
+        evSig,
+      });
       const coin: CoinRecord = {
         blindingKey: pc.blindingKey,
         coinPriv: pc.coinPriv,
         coinPub: pc.coinPub,
-        currentAmount: denom.value,
-        denomPubHash: denom.denomPubHash,
-        denomSig: {
-          cipher: DenomKeyType.Rsa,
-          rsa_signature: denomSigRsa,
-        },
+        currentAmount: ncd.value,
+        denomPubHash: ncd.denomPubHash,
+        denomSig,
         exchangeBaseUrl: oldCoin.exchangeBaseUrl,
         status: CoinStatus.Fresh,
         coinSource: {

-- 
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]