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-core: make basic backu


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: make basic backup work again
Date: Wed, 21 Sep 2022 12:40:17 +0200

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 28b4489be wallet-core: make basic backup work again
28b4489be is described below

commit 28b4489bead6cd88db1b91e0e0ae8b8e0d1d0007
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Sep 21 12:40:02 2022 +0200

    wallet-core: make basic backup work again
---
 .../test-wallet-backup-doublespend.ts              |  10 +-
 packages/taler-wallet-core/src/errors.ts           |  13 ++
 .../src/operations/backup/import.ts                | 216 +++++++--------------
 .../src/operations/backup/index.ts                 |   2 +
 packages/taler-wallet-core/src/operations/pay.ts   |  17 +-
 packages/taler-wallet-core/src/wallet.ts           |   3 +-
 6 files changed, 112 insertions(+), 149 deletions(-)

diff --git 
a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts
 
b/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts
index ec1d6417b..f5c9af07e 100644
--- 
a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts
+++ 
b/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts
@@ -31,9 +31,6 @@ import {
 } from "../harness/helpers.js";
 import { SyncService } from "../harness/sync";
 
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
 export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
   // Set up test environment
 
@@ -131,6 +128,13 @@ export async function runWalletBackupDoublespendTest(t: 
GlobalTestState) {
 
     // Make wallet pay for the order
 
+    {
+      console.log(
+        "wallet2 balance before preparePay:",
+        await wallet2.client.call(WalletApiOperation.GetBalances, {}),
+      );
+    }
+
     const preparePayResult = await wallet2.client.call(
       WalletApiOperation.PreparePayForUri,
       {
diff --git a/packages/taler-wallet-core/src/errors.ts 
b/packages/taler-wallet-core/src/errors.ts
index 62bde667d..0f4c480cd 100644
--- a/packages/taler-wallet-core/src/errors.ts
+++ b/packages/taler-wallet-core/src/errors.ts
@@ -113,6 +113,19 @@ function getDefaultHint(code: number): string {
   }
 }
 
+export class TalerProtocolViolationError<T = any> extends Error {
+  constructor(hint?: string) {
+    let msg: string;
+    if (hint) {
+      msg = `Taler protocol violation error (${hint})`;
+    } else {
+      msg = `Taler protocol violation error`;
+    }
+    super(msg);
+    Object.setPrototypeOf(this, TalerProtocolViolationError.prototype);
+  }
+}
+
 export class TalerError<T = any> extends Error {
   errorDetail: TalerErrorDetail & T;
   private constructor(d: TalerErrorDetail & T) {
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts 
b/packages/taler-wallet-core/src/operations/backup/import.ts
index 507a6cf10..20c7316c1 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -18,6 +18,7 @@ import {
   AgeRestriction,
   AmountJson,
   Amounts,
+  BackupCoin,
   BackupCoinSourceType,
   BackupDenomSel,
   BackupProposalStatus,
@@ -37,6 +38,7 @@ import {
 } from "@gnu-taler/taler-util";
 import {
   AbortStatus,
+  CoinRecord,
   CoinSource,
   CoinSourceType,
   CoinStatus,
@@ -65,6 +67,7 @@ import {
 } from "../../util/invariants.js";
 import { GetReadOnlyAccess, GetReadWriteAccess } from "../../util/query.js";
 import { RetryInfo } from "../../util/retries.js";
+import { makeCoinAvailable } from "../../wallet.js";
 import { getExchangeDetails } from "../exchanges.js";
 import { makeEventId, TombstoneTag } from "../transactions.js";
 import { provideBackupState } from "./state.js";
@@ -226,6 +229,71 @@ export interface BackupCryptoPrecomputedData {
   reservePrivToPub: Record<string, string>;
 }
 
+export async function importCoin(
+  ws: InternalWalletState,
+  tx: GetReadWriteAccess<{
+    coins: typeof WalletStoresV1.coins;
+    coinAvailability: typeof WalletStoresV1.coinAvailability;
+    denominations: typeof WalletStoresV1.denominations;
+  }>,
+  cryptoComp: BackupCryptoPrecomputedData,
+  args: {
+    backupCoin: BackupCoin;
+    exchangeBaseUrl: string;
+    denomPubHash: string;
+  },
+): Promise<void> {
+  const { backupCoin, exchangeBaseUrl, denomPubHash } = args;
+  const compCoin = cryptoComp.coinPrivToCompletedCoin[backupCoin.coin_priv];
+  checkLogicInvariant(!!compCoin);
+  const existingCoin = await tx.coins.get(compCoin.coinPub);
+  if (!existingCoin) {
+    let coinSource: CoinSource;
+    switch (backupCoin.coin_source.type) {
+      case BackupCoinSourceType.Refresh:
+        coinSource = {
+          type: CoinSourceType.Refresh,
+          oldCoinPub: backupCoin.coin_source.old_coin_pub,
+        };
+        break;
+      case BackupCoinSourceType.Tip:
+        coinSource = {
+          type: CoinSourceType.Tip,
+          coinIndex: backupCoin.coin_source.coin_index,
+          walletTipId: backupCoin.coin_source.wallet_tip_id,
+        };
+        break;
+      case BackupCoinSourceType.Withdraw:
+        coinSource = {
+          type: CoinSourceType.Withdraw,
+          coinIndex: backupCoin.coin_source.coin_index,
+          reservePub: backupCoin.coin_source.reserve_pub,
+          withdrawalGroupId: backupCoin.coin_source.withdrawal_group_id,
+        };
+        break;
+    }
+    const coinRecord: CoinRecord = {
+      blindingKey: backupCoin.blinding_key,
+      coinEvHash: compCoin.coinEvHash,
+      coinPriv: backupCoin.coin_priv,
+      currentAmount: Amounts.parseOrThrow(backupCoin.current_amount),
+      denomSig: backupCoin.denom_sig,
+      coinPub: compCoin.coinPub,
+      exchangeBaseUrl,
+      denomPubHash,
+      status: backupCoin.fresh ? CoinStatus.Fresh : CoinStatus.Dormant,
+      coinSource,
+      // FIXME!
+      maxAge: AgeRestriction.AGE_UNRESTRICTED,
+    };
+    if (coinRecord.status === CoinStatus.Fresh) {
+      await makeCoinAvailable(ws, tx, coinRecord);
+    } else {
+      await tx.coins.put(coinRecord);
+    }
+  }
+}
+
 export async function importBackup(
   ws: InternalWalletState,
   backupBlobArg: any,
@@ -241,6 +309,7 @@ export async function importBackup(
       x.exchangeDetails,
       x.exchanges,
       x.coins,
+      x.coinAvailability,
       x.denominations,
       x.purchases,
       x.proposals,
@@ -360,10 +429,6 @@ export async function importBackup(
             denomPubHash,
           ]);
           if (!existingDenom) {
-            logger.info(
-              `importing backup denomination: ${j2s(backupDenomination)}`,
-            );
-
             const value = Amounts.parseOrThrow(backupDenomination.value);
 
             await tx.denominations.put({
@@ -398,53 +463,11 @@ export async function importBackup(
             });
           }
           for (const backupCoin of backupDenomination.coins) {
-            const compCoin =
-              cryptoComp.coinPrivToCompletedCoin[backupCoin.coin_priv];
-            checkLogicInvariant(!!compCoin);
-            const existingCoin = await tx.coins.get(compCoin.coinPub);
-            if (!existingCoin) {
-              let coinSource: CoinSource;
-              switch (backupCoin.coin_source.type) {
-                case BackupCoinSourceType.Refresh:
-                  coinSource = {
-                    type: CoinSourceType.Refresh,
-                    oldCoinPub: backupCoin.coin_source.old_coin_pub,
-                  };
-                  break;
-                case BackupCoinSourceType.Tip:
-                  coinSource = {
-                    type: CoinSourceType.Tip,
-                    coinIndex: backupCoin.coin_source.coin_index,
-                    walletTipId: backupCoin.coin_source.wallet_tip_id,
-                  };
-                  break;
-                case BackupCoinSourceType.Withdraw:
-                  coinSource = {
-                    type: CoinSourceType.Withdraw,
-                    coinIndex: backupCoin.coin_source.coin_index,
-                    reservePub: backupCoin.coin_source.reserve_pub,
-                    withdrawalGroupId:
-                      backupCoin.coin_source.withdrawal_group_id,
-                  };
-                  break;
-              }
-              await tx.coins.put({
-                blindingKey: backupCoin.blinding_key,
-                coinEvHash: compCoin.coinEvHash,
-                coinPriv: backupCoin.coin_priv,
-                currentAmount: Amounts.parseOrThrow(backupCoin.current_amount),
-                denomSig: backupCoin.denom_sig,
-                coinPub: compCoin.coinPub,
-                exchangeBaseUrl: backupExchangeDetails.base_url,
-                denomPubHash,
-                status: backupCoin.fresh
-                  ? CoinStatus.Fresh
-                  : CoinStatus.Dormant,
-                coinSource,
-                // FIXME!
-                maxAge: AgeRestriction.AGE_UNRESTRICTED,
-              });
-            }
+            await importCoin(ws, tx, cryptoComp, {
+              backupCoin,
+              denomPubHash,
+              exchangeBaseUrl: backupExchangeDetails.base_url,
+            });
           }
         }
 
@@ -532,97 +555,6 @@ export async function importBackup(
             timestampFinish: backupWg.timestamp_finish,
           });
         }
-
-        // FIXME: import reserves with new schema
-
-        // for (const backupReserve of backupExchangeDetails.reserves) {
-        //   const reservePub =
-        //     cryptoComp.reservePrivToPub[backupReserve.reserve_priv];
-        //   const ts = makeEventId(TombstoneTag.DeleteReserve, reservePub);
-        //   if (tombstoneSet.has(ts)) {
-        //     continue;
-        //   }
-        //   checkLogicInvariant(!!reservePub);
-        //   const existingReserve = await tx.reserves.get(reservePub);
-        //   const instructedAmount = Amounts.parseOrThrow(
-        //     backupReserve.instructed_amount,
-        //   );
-        //   if (!existingReserve) {
-        //     let bankInfo: ReserveBankInfo | undefined;
-        //     if (backupReserve.bank_info) {
-        //       bankInfo = {
-        //         exchangePaytoUri: 
backupReserve.bank_info.exchange_payto_uri,
-        //         statusUrl: backupReserve.bank_info.status_url,
-        //         confirmUrl: backupReserve.bank_info.confirm_url,
-        //       };
-        //     }
-        //     await tx.reserves.put({
-        //       currency: instructedAmount.currency,
-        //       instructedAmount,
-        //       exchangeBaseUrl: backupExchangeDetails.base_url,
-        //       reservePub,
-        //       reservePriv: backupReserve.reserve_priv,
-        //       bankInfo,
-        //       timestampCreated: backupReserve.timestamp_created,
-        //       timestampBankConfirmed:
-        //         backupReserve.bank_info?.timestamp_bank_confirmed,
-        //       timestampReserveInfoPosted:
-        //         backupReserve.bank_info?.timestamp_reserve_info_posted,
-        //       senderWire: backupReserve.sender_wire,
-        //       retryInfo: RetryInfo.reset(),
-        //       lastError: undefined,
-        //       initialWithdrawalGroupId:
-        //         backupReserve.initial_withdrawal_group_id,
-        //       initialWithdrawalStarted:
-        //         backupReserve.withdrawal_groups.length > 0,
-        //       // FIXME!
-        //       reserveStatus: ReserveRecordStatus.QueryingStatus,
-        //       initialDenomSel: await getDenomSelStateFromBackup(
-        //         tx,
-        //         backupExchangeDetails.base_url,
-        //         backupReserve.initial_selected_denoms,
-        //       ),
-        //       // FIXME!
-        //       operationStatus: OperationStatus.Pending,
-        //     });
-        //   }
-        //   for (const backupWg of backupReserve.withdrawal_groups) {
-        //     const ts = makeEventId(
-        //       TombstoneTag.DeleteWithdrawalGroup,
-        //       backupWg.withdrawal_group_id,
-        //     );
-        //     if (tombstoneSet.has(ts)) {
-        //       continue;
-        //     }
-        //     const existingWg = await tx.withdrawalGroups.get(
-        //       backupWg.withdrawal_group_id,
-        //     );
-        //     if (!existingWg) {
-        //       await tx.withdrawalGroups.put({
-        //         denomsSel: await getDenomSelStateFromBackup(
-        //           tx,
-        //           backupExchangeDetails.base_url,
-        //           backupWg.selected_denoms,
-        //         ),
-        //         exchangeBaseUrl: backupExchangeDetails.base_url,
-        //         lastError: undefined,
-        //         rawWithdrawalAmount: Amounts.parseOrThrow(
-        //           backupWg.raw_withdrawal_amount,
-        //         ),
-        //         reservePub,
-        //         retryInfo: RetryInfo.reset(),
-        //         secretSeed: backupWg.secret_seed,
-        //         timestampStart: backupWg.timestamp_created,
-        //         timestampFinish: backupWg.timestamp_finish,
-        //         withdrawalGroupId: backupWg.withdrawal_group_id,
-        //         denomSelUid: backupWg.selected_denoms_id,
-        //         operationStatus: backupWg.timestamp_finish
-        //           ? OperationStatus.Finished
-        //           : OperationStatus.Pending,
-        //       });
-        //     }
-        //   }
-        // }
       }
 
       for (const backupProposal of backupBlob.proposals) {
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts 
b/packages/taler-wallet-core/src/operations/backup/index.ts
index b69c0b7b7..fc84ce4ef 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -482,6 +482,8 @@ export async function processBackupForProvider(
     throw Error("unknown backup provider");
   }
 
+  logger.info(`running backup for provider ${backupProviderBaseUrl}`);
+
   return await runBackupCycleForProvider(ws, {
     backupProviderBaseUrl: provider.baseUrl,
     retryAfterPayment: true,
diff --git a/packages/taler-wallet-core/src/operations/pay.ts 
b/packages/taler-wallet-core/src/operations/pay.ts
index 415100160..6757b79b4 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -78,6 +78,7 @@ import {
   makeErrorDetail,
   makePendingOperationFailedError,
   TalerError,
+  TalerProtocolViolationError,
 } from "../errors.js";
 import {
   EXCHANGE_COINS_LOCK,
@@ -752,7 +753,7 @@ async function handleInsufficientFunds(
     return;
   }
 
-  const brokenCoinPub = (err as any).coin_pub;
+  logger.trace(`got error details: ${j2s(err)}`);
 
   const exchangeReply = (err as any).exchange_reply;
   if (
@@ -766,7 +767,12 @@ async function handleInsufficientFunds(
     throw Error(`unable to handle /pay error response 
(${exchangeReply.code})`);
   }
 
-  logger.trace(`got error details: ${j2s(err)}`);
+  const brokenCoinPub = (exchangeReply as any).coin_pub;
+  logger.trace(`excluded broken coin pub=${brokenCoinPub}`);
+
+  if (!brokenCoinPub) {
+    throw new TalerProtocolViolationError();
+  }
 
   const { contractData } = proposal.download;
 
@@ -1146,6 +1152,8 @@ export async function selectPayCoinsNew(
     req,
   );
 
+  // logger.trace(`candidate denoms: ${j2s(candidateDenoms)}`);
+
   const coinPubs: string[] = [];
   const coinContributions: AmountJson[] = [];
   const currency = contractTermsAmount.currency;
@@ -1201,6 +1209,9 @@ export async function selectPayCoinsNew(
 
   const finalSel = selectedDenom;
 
+  logger.trace(`coin selection request ${j2s(req)}`);
+  logger.trace(`selected coins (via denoms) for payment: ${j2s(finalSel)}`);
+
   await ws.db
     .mktx((x) => [x.coins, x.denominations])
     .runReadOnly(async (tx) => {
@@ -1301,7 +1312,7 @@ export async function checkPaymentByProposalId(
     });
 
     if (!res) {
-      logger.info("not confirming payment, insufficient coins");
+      logger.info("not allowing payment, insufficient coins");
       return {
         status: PreparePayResultType.InsufficientBalance,
         contractTerms: d.contractTermsRaw,
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 2e362da6e..c91a96841 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -221,7 +221,7 @@ import {
   HttpRequestLibrary,
   readSuccessResponseJsonOrThrow,
 } from "./util/http.js";
-import { checkDbInvariant } from "./util/invariants.js";
+import { checkDbInvariant, checkLogicInvariant } from "./util/invariants.js";
 import {
   AsyncCondition,
   OpenedPromise,
@@ -812,6 +812,7 @@ export async function makeCoinAvailable(
   }>,
   coinRecord: CoinRecord,
 ): Promise<void> {
+  checkLogicInvariant(coinRecord.status === CoinStatus.Fresh);
   const existingCoin = await tx.coins.get(coinRecord.coinPub);
   if (existingCoin) {
     return;

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