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: put signing keys


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: put signing keys in separate object store
Date: Sat, 15 Oct 2022 16:03:51 +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 a41d1ee53 wallet-core: put signing keys in separate object store
a41d1ee53 is described below

commit a41d1ee53e1dc6af0b54f085053278e039cda8dc
Author: Florian Dold <florian@dold.me>
AuthorDate: Sat Oct 15 16:03:48 2022 +0200

    wallet-core: put signing keys in separate object store
---
 packages/idb-bridge/src/MemoryBackend.test.ts      | 40 ++++++++++
 packages/taler-wallet-core/src/db.ts               | 49 +++++++++---
 .../src/operations/backup/export.ts                | 23 ++++--
 .../src/operations/backup/import.ts                | 30 ++++----
 .../taler-wallet-core/src/operations/exchanges.ts  | 90 +++++++++++++++-------
 packages/taler-wallet-core/src/util/query.ts       |  1 +
 6 files changed, 175 insertions(+), 58 deletions(-)

diff --git a/packages/idb-bridge/src/MemoryBackend.test.ts 
b/packages/idb-bridge/src/MemoryBackend.test.ts
index b36143aa2..8a544a201 100644
--- a/packages/idb-bridge/src/MemoryBackend.test.ts
+++ b/packages/idb-bridge/src/MemoryBackend.test.ts
@@ -26,6 +26,7 @@ import {
 import {
   IDBCursorDirection,
   IDBCursorWithValue,
+  IDBDatabase,
   IDBKeyRange,
   IDBValidKey,
 } from "./idbtypes.js";
@@ -439,6 +440,45 @@ test("update with non-existent index values", async (t) => 
{
   t.pass();
 });
 
+test("delete from unique index", async (t) => {
+  const backend = new MemoryBackend();
+  backend.enableTracing = true;
+  const idb = new BridgeIDBFactory(backend);
+  const request = idb.open("mydb");
+  request.onupgradeneeded = () => {
+    const db = request.result as IDBDatabase;
+    const store = db.createObjectStore("bla", { keyPath: "x" });
+    store.createIndex("by_yz", ["y", "z"], {
+      unique: true,
+    });
+  };
+
+  const db: BridgeIDBDatabase = await promiseFromRequest(request);
+
+  t.is(db.name, "mydb");
+
+  {
+    const tx = db.transaction("bla", "readwrite");
+    const store = tx.objectStore("bla");
+    store.put({ x: 0, y: "a", z: 42 });
+    const index = store.index("by_yz");
+    const indRes = await promiseFromRequest(index.get(["a", 42]));
+    t.is(indRes.x, 0);
+    const res = await promiseFromRequest(store.get(0));
+    t.is(res.z, 42);
+    await promiseFromTransaction(tx);
+  }
+
+  {
+    const tx = db.transaction("bla", "readwrite");
+    const store = tx.objectStore("bla");
+    store.put({ x: 0, y: "a", z: 42, extra: 123 });
+    await promiseFromTransaction(tx);
+  }
+
+  t.pass();
+});
+
 test("range queries", async (t) => {
   const backend = new MemoryBackend();
   backend.enableTracing = true;
diff --git a/packages/taler-wallet-core/src/db.ts 
b/packages/taler-wallet-core/src/db.ts
index b785efed8..dd21aa037 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -51,6 +51,8 @@ import {
   TransactionIdStr,
   CoinRefreshRequest,
   CoinStatus,
+  EddsaPublicKeyString,
+  EddsaSignatureString,
 } from "@gnu-taler/taler-util";
 import { RetryInfo, RetryTags } from "./util/retries.js";
 import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
@@ -409,11 +411,26 @@ export namespace DenominationRecord {
   }
 }
 
+export interface ExchangeSignkeysRecord {
+  stampStart: TalerProtocolTimestamp;
+  stampExpire: TalerProtocolTimestamp;
+  stampEnd: TalerProtocolTimestamp;
+  signkeyPub: EddsaPublicKeyString;
+  masterSig: EddsaSignatureString;
+
+  /**
+   * Exchange details that thiis signkeys record belongs to.
+   */
+  exchangeDetailsRowId: number;
+}
+
 /**
  * Exchange details for a particular
  * (exchangeBaseUrl, masterPublicKey, currency) tuple.
  */
 export interface ExchangeDetailsRecord {
+  rowId?: number;
+
   /**
    * Master public key of the exchange.
    */
@@ -445,14 +462,6 @@ export interface ExchangeDetailsRecord {
    */
   globalFees: ExchangeGlobalFees[];
 
-  /**
-   * Signing keys we got from the exchange, can also contain
-   * older signing keys that are not returned by /keys anymore.
-   *
-   * FIXME:  Should this be put into a separate object store?
-   */
-  signingKeys: ExchangeSignKeyJson[];
-
   /**
    * Etag of the current ToS of the exchange.
    */
@@ -1892,9 +1901,29 @@ export const WalletStoresV1 = {
   exchangeDetails: describeStore(
     "exchangeDetails",
     describeContents<ExchangeDetailsRecord>({
-      keyPath: ["exchangeBaseUrl", "currency", "masterPublicKey"],
+      keyPath: "rowId",
+      autoIncrement: true,
     }),
-    {},
+    {
+      byPointer: describeIndex(
+        "byDetailsPointer",
+        ["exchangeBaseUrl", "currency", "masterPublicKey"],
+        {
+          unique: true,
+        },
+      ),
+    },
+  ),
+  exchangeSignkeys: describeStore(
+    "exchangeSignKeys",
+    describeContents<ExchangeSignkeysRecord>({
+      keyPath: ["exchangeDetailsRowId", "signkeyPub"],
+    }),
+    {
+      byExchangeDetailsRowId: describeIndex("byExchangeDetailsRowId", [
+        "exchangeDetailsRowId",
+      ]),
+    },
   ),
   refreshGroups: describeStore(
     "refreshGroups",
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts 
b/packages/taler-wallet-core/src/operations/backup/export.ts
index 1472b1b90..b0f1d6ce1 100644
--- a/packages/taler-wallet-core/src/operations/backup/export.ts
+++ b/packages/taler-wallet-core/src/operations/backup/export.ts
@@ -35,6 +35,7 @@ import {
   BackupDenomination,
   BackupExchange,
   BackupExchangeDetails,
+  BackupExchangeSignKey,
   BackupExchangeWireFee,
   BackupOperationStatus,
   BackupPayInfo,
@@ -74,6 +75,7 @@ import {
 } from "../../db.js";
 import { InternalWalletState } from "../../internal-wallet-state.js";
 import { assertUnreachable } from "../../util/assertUnreachable.js";
+import { checkDbInvariant } from "../../util/invariants.js";
 import { getWalletBackupState, provideBackupState } from "./state.js";
 
 const logger = new Logger("backup/export.ts");
@@ -87,6 +89,7 @@ export async function exportBackup(
       x.config,
       x.exchanges,
       x.exchangeDetails,
+      x.exchangeSignkeys,
       x.coins,
       x.contractTerms,
       x.denominations,
@@ -324,6 +327,18 @@ export async function exportBackup(
             });
           }
         });
+        checkDbInvariant(ex.rowId != null);
+        const exchangeSk =
+          await tx.exchangeSignKeys.indexes.byExchangeDetailsRowId.getAll(
+            ex.rowId,
+          );
+        let signingKeys: BackupExchangeSignKey[] = exchangeSk.map((x) => ({
+          key: x.signkeyPub,
+          master_sig: x.masterSig,
+          stamp_end: x.stampEnd,
+          stamp_expire: x.stampExpire,
+          stamp_start: x.stampStart,
+        }));
 
         backupExchangeDetails.push({
           base_url: ex.exchangeBaseUrl,
@@ -341,13 +356,7 @@ export async function exportBackup(
           currency: ex.currency,
           protocol_version: ex.protocolVersionRange,
           wire_fees: wireFees,
-          signing_keys: ex.signingKeys.map((x) => ({
-            key: x.key,
-            master_sig: x.master_sig,
-            stamp_end: x.stamp_end,
-            stamp_expire: x.stamp_expire,
-            stamp_start: x.stamp_start,
-          })),
+          signing_keys: signingKeys,
           global_fees: ex.globalFees.map((x) => ({
             accountFee: Amounts.stringify(x.accountFee),
             historyFee: Amounts.stringify(x.historyFee),
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts 
b/packages/taler-wallet-core/src/operations/backup/import.ts
index 9c5eea9af..f08d152a5 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -62,7 +62,12 @@ import { InternalWalletState } from 
"../../internal-wallet-state.js";
 import { assertUnreachable } from "../../util/assertUnreachable.js";
 import { checkLogicInvariant } from "../../util/invariants.js";
 import { GetReadOnlyAccess, GetReadWriteAccess } from "../../util/query.js";
-import { makeCoinAvailable, makeTombstoneId, makeTransactionId, TombstoneTag } 
from "../common.js";
+import {
+  makeCoinAvailable,
+  makeTombstoneId,
+  makeTransactionId,
+  TombstoneTag,
+} from "../common.js";
 import { getExchangeDetails } from "../exchanges.js";
 import { extractContractData } from "../pay-merchant.js";
 import { provideBackupState } from "./state.js";
@@ -360,11 +365,12 @@ export async function importBackup(
       }
 
       for (const backupExchangeDetails of backupBlob.exchange_details) {
-        const existingExchangeDetails = await tx.exchangeDetails.get([
-          backupExchangeDetails.base_url,
-          backupExchangeDetails.currency,
-          backupExchangeDetails.master_public_key,
-        ]);
+        const existingExchangeDetails =
+          await tx.exchangeDetails.indexes.byPointer.get([
+            backupExchangeDetails.base_url,
+            backupExchangeDetails.currency,
+            backupExchangeDetails.master_public_key,
+          ]);
 
         if (!existingExchangeDetails) {
           const wireInfo: WireInfo = {
@@ -422,13 +428,6 @@ export async function importBackup(
               purseTimeout: x.purseTimeout,
               startDate: x.startDate,
             })),
-            signingKeys: backupExchangeDetails.signing_keys.map((x) => ({
-              key: x.key,
-              master_sig: x.master_sig,
-              stamp_end: x.stamp_end,
-              stamp_expire: x.stamp_expire,
-              stamp_start: x.stamp_start,
-            })),
           });
         }
 
@@ -789,7 +788,10 @@ export async function importBackup(
       }
 
       for (const backupTip of backupBlob.tips) {
-        const ts = makeTombstoneId(TombstoneTag.DeleteTip, 
backupTip.wallet_tip_id);
+        const ts = makeTombstoneId(
+          TombstoneTag.DeleteTip,
+          backupTip.wallet_tip_id,
+        );
         if (tombstoneSet.has(ts)) {
           continue;
         }
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts 
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 6569cb394..e89364ad1 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -64,6 +64,7 @@ import {
   readSuccessResponseJsonOrThrow,
   readSuccessResponseTextOrThrow,
 } from "../util/http.js";
+import { checkDbInvariant } from "../util/invariants.js";
 import {
   DbAccess,
   GetReadOnlyAccess,
@@ -168,7 +169,11 @@ export async function getExchangeDetails(
     return;
   }
   const { currency, masterPublicKey } = dp;
-  return await tx.exchangeDetails.get([r.baseUrl, currency, masterPublicKey]);
+  return await tx.exchangeDetails.indexes.byPointer.get([
+    r.baseUrl,
+    currency,
+    masterPublicKey,
+  ]);
 }
 
 getExchangeDetails.makeContext = (db: DbAccess<typeof WalletStoresV1>) =>
@@ -205,7 +210,7 @@ export async function updateExchangeTermsOfService(
 
 /**
  * Mark a ToS version as accepted by the user.
- * 
+ *
  * @param etag version of the ToS to accept, or current ToS version of not 
given
  */
 export async function acceptExchangeTermsOfService(
@@ -568,10 +573,14 @@ export async function updateExchangeFromUrlHandler(
 
   const now = AbsoluteTime.now();
   baseUrl = canonicalizeBaseUrl(baseUrl);
-
+  let isNewExchange = true;
   const { exchange, exchangeDetails } = await ws.db
     .mktx((x) => [x.exchanges, x.exchangeDetails])
     .runReadWrite(async (tx) => {
+      let oldExch = await tx.exchanges.get(baseUrl);
+      if (oldExch) {
+        isNewExchange = false;
+      }
       return provideExchangeRecordInTx(ws, tx, baseUrl, now);
     });
 
@@ -637,10 +646,13 @@ export async function updateExchangeFromUrlHandler(
 
   logger.trace("updating exchange info in database");
 
+  let detailsPointerChanged = false;
+
   const updated = await ws.db
     .mktx((x) => [
       x.exchanges,
       x.exchangeDetails,
+      x.exchangeSignkeys,
       x.denominations,
       x.coins,
       x.refreshGroups,
@@ -652,42 +664,63 @@ export async function updateExchangeFromUrlHandler(
         logger.warn(`exchange ${baseUrl} no longer present`);
         return;
       }
-      let details = await getExchangeDetails(tx, r.baseUrl);
-      if (details) {
+      let existingDetails = await getExchangeDetails(tx, r.baseUrl);
+      let acceptedTosEtag = undefined;
+      if (!existingDetails) {
+        detailsPointerChanged = true;
+      }
+      if (existingDetails) {
+        acceptedTosEtag = existingDetails.tosAccepted?.etag;
+        if (existingDetails.masterPublicKey !== keysInfo.masterPublicKey) {
+          detailsPointerChanged = true;
+        }
+        if (existingDetails.currency !== keysInfo.currency) {
+          detailsPointerChanged = true;
+        }
         // FIXME: We need to do some consistency checks!
       }
-      // FIXME: validate signing keys and merge with old set
-      details = {
+      let existingTosAccepted = existingDetails?.tosAccepted;
+      const newDetails = {
+        rowId: existingDetails?.rowId,
         auditors: keysInfo.auditors,
         currency: keysInfo.currency,
         masterPublicKey: keysInfo.masterPublicKey,
         protocolVersionRange: keysInfo.protocolVersion,
-        signingKeys: keysInfo.signingKeys,
         reserveClosingDelay: keysInfo.reserveClosingDelay,
         globalFees,
         exchangeBaseUrl: r.baseUrl,
         wireInfo,
-        tosCurrentEtag: tosDownload.tosContentType,
-        tosAccepted: tosHasBeenAccepted
-          ? {
-              etag: tosDownload.tosEtag,
-              timestamp: TalerProtocolTimestamp.now(),
-            }
-          : undefined,
+        tosCurrentEtag: tosDownload.tosEtag,
+        tosAccepted: existingTosAccepted,
       };
-      // FIXME: only update if pointer got updated
       r.lastUpdate = TalerProtocolTimestamp.now();
       r.nextUpdate = keysInfo.expiry;
       // New denominations might be available.
       r.nextRefreshCheck = TalerProtocolTimestamp.now();
-      r.detailsPointer = {
-        currency: details.currency,
-        masterPublicKey: details.masterPublicKey,
-        // FIXME: only change if pointer really changed
-        updateClock: TalerProtocolTimestamp.now(),
-      };
+      if (detailsPointerChanged) {
+        r.detailsPointer = {
+          currency: newDetails.currency,
+          masterPublicKey: newDetails.masterPublicKey,
+          updateClock: TalerProtocolTimestamp.now(),
+        };
+      }
       await tx.exchanges.put(r);
-      await tx.exchangeDetails.put(details);
+      logger.info(`existing details ${j2s(existingDetails)}`);
+      logger.info(`inserting new details ${j2s(newDetails)}`);
+      const drRowId = await tx.exchangeDetails.put(newDetails);
+      checkDbInvariant(typeof drRowId.key === "number");
+
+      for (const sk of keysInfo.signingKeys) {
+        // FIXME: validate signing keys before inserting them
+        await tx.exchangeSignKeys.put({
+          exchangeDetailsRowId: drRowId.key,
+          masterSig: sk.master_sig,
+          signkeyPub: sk.key,
+          stampEnd: sk.stamp_end,
+          stampExpire: sk.stamp_expire,
+          stampStart: sk.stamp_start,
+        });
+      }
 
       logger.info("updating denominations in database");
       const currentDenomSet = new Set<string>(
@@ -773,7 +806,7 @@ export async function updateExchangeFromUrlHandler(
       }
       return {
         exchange: r,
-        exchangeDetails: details,
+        exchangeDetails: newDetails,
       };
     });
 
@@ -791,9 +824,12 @@ export async function updateExchangeFromUrlHandler(
 
   logger.trace("done updating exchange info in database");
 
-  ws.notify({
-    type: NotificationType.ExchangeAdded,
-  });
+  if (isNewExchange) {
+    ws.notify({
+      type: NotificationType.ExchangeAdded,
+    });
+  }
+
   return {
     type: OperationAttemptResultType.Finished,
     result: {
diff --git a/packages/taler-wallet-core/src/util/query.ts 
b/packages/taler-wallet-core/src/util/query.ts
index 47f38a3a1..9e960821d 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -494,6 +494,7 @@ function runTx<Arg, Res>(
         msg = "Transaction aborted (no DB error)";
       }
       logger.error(msg);
+      logger.error(`${stack.stack}`);
       reject(new TransactionAbortedError(msg));
     };
     const resP = Promise.resolve().then(() => f(arg, tx));

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