gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: anastasis: use new truth API


From: gnunet
Subject: [taler-wallet-core] branch master updated: anastasis: use new truth API
Date: Wed, 06 Apr 2022 13:19:37 +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 f33d9dad anastasis: use new truth API
f33d9dad is described below

commit f33d9dad4782c81df9056e81da0e1a4174ae3298
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Apr 6 13:19:34 2022 +0200

    anastasis: use new truth API
---
 packages/anastasis-core/src/index.ts         | 232 ++++++++++++++-------------
 packages/anastasis-core/src/reducer-types.ts |   2 -
 packages/anastasis-webui/src/hooks/async.ts  |  21 ++-
 packages/taler-util/src/taler-error-codes.ts |  98 +++++++++++
 4 files changed, 235 insertions(+), 118 deletions(-)

diff --git a/packages/anastasis-core/src/index.ts 
b/packages/anastasis-core/src/index.ts
index d1afc706..a355eaa5 100644
--- a/packages/anastasis-core/src/index.ts
+++ b/packages/anastasis-core/src/index.ts
@@ -867,6 +867,51 @@ async function pollChallenges(
   return state;
 }
 
+async function getResponseHash(
+  truth: EscrowMethod,
+  solveRequest: ActionArgsSolveChallengeRequest,
+): Promise<string> {
+  let respHash: string;
+  switch (truth.escrow_type) {
+    case ChallengeType.Question: {
+      if ("answer" in solveRequest) {
+        respHash = await secureAnswerHash(
+          solveRequest.answer,
+          truth.uuid,
+          truth.truth_salt,
+        );
+      } else {
+        throw Error("unsupported answer request");
+      }
+      break;
+    }
+    case ChallengeType.Email:
+    case ChallengeType.Sms:
+    case ChallengeType.Post:
+    case ChallengeType.Iban:
+    case ChallengeType.Totp: {
+      if ("answer" in solveRequest) {
+        const s = solveRequest.answer.trim().replace(/^A-/, "");
+        let pin: number;
+        try {
+          pin = Number.parseInt(s);
+        } catch (e) {
+          throw Error("invalid pin format");
+        }
+        respHash = await pinAnswerHash(pin);
+      } else if ("pin" in solveRequest) {
+        respHash = await pinAnswerHash(solveRequest.pin);
+      } else {
+        throw Error("unsupported answer request");
+      }
+      break;
+    }
+    default:
+      throw Error(`unsupported challenge type "${truth.escrow_type}""`);
+  }
+  return respHash;
+}
+
 /**
  * Request a truth, optionally with a challenge solution
  * provided by the user.
@@ -874,61 +919,26 @@ async function pollChallenges(
 async function requestTruth(
   state: ReducerStateRecovery,
   truth: EscrowMethod,
-  solveRequest?: ActionArgsSolveChallengeRequest,
+  solveRequest: ActionArgsSolveChallengeRequest,
 ): Promise<ReducerStateRecovery | ReducerStateError> {
-  const url = new URL(`/truth/${truth.uuid}`, truth.url);
-
-  if (solveRequest) {
-    logger.info(`handling solve request ${j2s(solveRequest)}`);
-    let respHash: string;
-    switch (truth.escrow_type) {
-      case ChallengeType.Question: {
-        if ("answer" in solveRequest) {
-          respHash = await secureAnswerHash(
-            solveRequest.answer,
-            truth.uuid,
-            truth.truth_salt,
-          );
-        } else {
-          throw Error("unsupported answer request");
-        }
-        break;
-      }
-      case ChallengeType.Email:
-      case ChallengeType.Sms:
-      case ChallengeType.Post:
-      case ChallengeType.Iban:
-      case ChallengeType.Totp: {
-        if ("answer" in solveRequest) {
-          const s = solveRequest.answer.trim().replace(/^A-/, "");
-          let pin: number;
-          try {
-            pin = Number.parseInt(s);
-          } catch (e) {
-            throw Error("invalid pin format");
-          }
-          respHash = await pinAnswerHash(pin);
-        } else if ("pin" in solveRequest) {
-          respHash = await pinAnswerHash(solveRequest.pin);
-        } else {
-          throw Error("unsupported answer request");
-        }
-        break;
-      }
-      default:
-        throw Error(`unsupported challenge type "${truth.escrow_type}""`);
-    }
-    url.searchParams.set("response", respHash);
-  }
+  const url = new URL(`/truth/${truth.uuid}/solve`, truth.url);
+
+  const hresp = await getResponseHash(truth, solveRequest);
 
   const resp = await fetch(url.href, {
+    method: "POST",
     headers: {
-      "Anastasis-Truth-Decryption-Key": truth.truth_key,
+      Accept: "application/json",
+      "Content-Type": "application/json",
     },
+    body: JSON.stringify({
+      truth_decryption_key: truth.truth_key,
+      h_response: hresp,
+    }),
   });
 
   logger.info(
-    `got GET /truth response from ${truth.url}, http status ${resp.status}`,
+    `got POST /truth/.../solve response from ${truth.url}, http status 
${resp.status}`,
   );
 
   if (resp.status === HttpStatusCode.Ok) {
@@ -975,66 +985,6 @@ async function requestTruth(
     return tryRecoverSecret(newState);
   }
 
-  if (resp.status === HttpStatusCode.Forbidden) {
-    const body = await resp.json();
-    if (
-      body.code === TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED
-    ) {
-      return {
-        ...state,
-        recovery_state: RecoveryStates.ChallengeSolving,
-        challenge_feedback: {
-          ...state.challenge_feedback,
-          [truth.uuid]: {
-            state: ChallengeFeedbackStatus.Pending,
-          },
-        },
-      };
-    }
-    return {
-      ...state,
-      recovery_state: RecoveryStates.ChallengeSolving,
-      challenge_feedback: {
-        ...state.challenge_feedback,
-        [truth.uuid]: {
-          state: ChallengeFeedbackStatus.Message,
-          message: body.hint ?? "Challenge should be solved",
-        },
-      },
-    };
-  }
-
-  if (resp.status === HttpStatusCode.Accepted) {
-    const body = await resp.json();
-    logger.info(`got body ${j2s(body)}`);
-    if (body.method === "iban") {
-      const b = body as IbanExternalAuthResponse;
-      return {
-        ...state,
-        recovery_state: RecoveryStates.ChallengeSolving,
-        challenge_feedback: {
-          ...state.challenge_feedback,
-          [truth.uuid]: {
-            state: ChallengeFeedbackStatus.AuthIban,
-            answer_code: b.answer_code,
-            business_name: b.details.business_name,
-            challenge_amount: b.details.challenge_amount,
-            credit_iban: b.details.credit_iban,
-            wire_transfer_subject: b.details.wire_transfer_subject,
-            details: b.details,
-            method: "iban",
-          },
-        },
-      };
-    } else {
-      return {
-        code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED,
-        hint: "unknown external authentication method",
-        http_status: resp.status,
-      } as ReducerStateError;
-    }
-  }
-
   return {
     code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED,
     hint: "got unexpected /truth/ response status",
@@ -1053,6 +1003,7 @@ async function solveChallenge(
   if (!truth) {
     throw Error("truth for challenge not found");
   }
+
   return requestTruth(state, truth, ta);
 }
 
@@ -1096,7 +1047,58 @@ async function selectChallenge(
     throw "truth for challenge not found";
   }
 
-  return requestTruth({ ...state, selected_challenge_uuid: ta.uuid }, truth);
+  const url = new URL(`/truth/${truth.uuid}/challenge`, truth.url);
+
+  if (truth.escrow_type === ChallengeType.Question) {
+    return {
+      ...state,
+      recovery_state: RecoveryStates.ChallengeSolving,
+      selected_challenge_uuid: truth.uuid,
+      challenge_feedback: {
+        ...state.challenge_feedback,
+        [truth.uuid]: {
+          state: ChallengeFeedbackStatus.Pending,
+        },
+      },
+    };
+  }
+
+  const resp = await fetch(url.href, {
+    method: "POST",
+    headers: {
+      Accept: "application/json",
+      "Content-Type": "application/json",
+    },
+    body: JSON.stringify({
+      truth_decryption_key: truth.truth_key,
+    }),
+  });
+
+  logger.info(
+    `got GET /truth/.../challenge response from ${truth.url}, http status 
${resp.status}`,
+  );
+
+  if (resp.status === HttpStatusCode.Ok) {
+    return {
+      ...state,
+      recovery_state: RecoveryStates.ChallengeSolving,
+      selected_challenge_uuid: truth.uuid,
+      challenge_feedback: {
+        ...state.challenge_feedback,
+        [truth.uuid]: {
+          state: ChallengeFeedbackStatus.Pending,
+        },
+      },
+    };
+  }
+
+  // FIXME: look at response, include in challenge_feedback!
+
+  return {
+    code: TalerErrorCode.ANASTASIS_TRUTH_CHALLENGE_FAILED,
+    hint: "got unexpected /truth/.../challenge response status",
+    http_status: resp.status,
+  } as ReducerStateError;
 }
 
 async function backupSelectContinent(
@@ -1140,15 +1142,15 @@ interface TransitionImpl<S, T> {
   handler: (s: S, args: T) => Promise<S | ReducerStateError>;
 }
 
-interface Transition<S, T> {
-  [x: string]: TransitionImpl<S, T>;
+interface Transition<S> {
+  [x: string]: TransitionImpl<S, any>;
 }
 
 function transition<S, T>(
   action: string,
   argCodec: Codec<T>,
   handler: (s: S, args: T) => Promise<S | ReducerStateError>,
-): Transition<S, T> {
+): Transition<S> {
   return {
     [action]: {
       argCodec,
@@ -1160,7 +1162,7 @@ function transition<S, T>(
 function transitionBackupJump(
   action: string,
   st: BackupStates,
-): Transition<ReducerStateBackup, void> {
+): Transition<ReducerStateBackup> {
   return {
     [action]: {
       argCodec: codecForAny(),
@@ -1172,7 +1174,7 @@ function transitionBackupJump(
 function transitionRecoveryJump(
   action: string,
   st: RecoveryStates,
-): Transition<ReducerStateRecovery, void> {
+): Transition<ReducerStateRecovery> {
   return {
     [action]: {
       argCodec: codecForAny(),
@@ -1440,7 +1442,7 @@ async function updateSecretExpiration(
 
 const backupTransitions: Record<
   BackupStates,
-  Transition<ReducerStateBackup, any>
+  Transition<ReducerStateBackup>
 > = {
   [BackupStates.ContinentSelecting]: {
     ...transition(
@@ -1511,7 +1513,7 @@ const backupTransitions: Record<
 
 const recoveryTransitions: Record<
   RecoveryStates,
-  Transition<ReducerStateRecovery, any>
+  Transition<ReducerStateRecovery>
 > = {
   [RecoveryStates.ContinentSelecting]: {
     ...transition(
diff --git a/packages/anastasis-core/src/reducer-types.ts 
b/packages/anastasis-core/src/reducer-types.ts
index 2a869fe4..4682eddb 100644
--- a/packages/anastasis-core/src/reducer-types.ts
+++ b/packages/anastasis-core/src/reducer-types.ts
@@ -6,9 +6,7 @@ import {
   codecForNumber,
   codecForString,
   codecForTimestamp,
-  Duration,
   TalerProtocolTimestamp,
-  AbsoluteTime,
 } from "@gnu-taler/taler-util";
 import { ChallengeFeedback } from "./challenge-feedback-types.js";
 import { KeyShare } from "./crypto.js";
diff --git a/packages/anastasis-webui/src/hooks/async.ts 
b/packages/anastasis-webui/src/hooks/async.ts
index 0fc19755..5235e1e3 100644
--- a/packages/anastasis-webui/src/hooks/async.ts
+++ b/packages/anastasis-webui/src/hooks/async.ts
@@ -18,7 +18,7 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { useState } from "preact/hooks";
+import { useCallback, useEffect, useRef, useState } from "preact/hooks";
 // import { cancelPendingRequest } from "./backend";
 
 export interface Options {
@@ -34,6 +34,17 @@ export interface AsyncOperationApi<T> {
   error: string | undefined;
 }
 
+export function useIsMounted() {
+  const isMountedRef = useRef(true);
+  const isMounted = useCallback(() => isMountedRef.current, []);
+
+  useEffect(() => {
+    return () => void (isMountedRef.current = false);
+  }, []);
+
+  return isMounted;
+}
+
 export function useAsync<T>(
   fn?: (...args: any) => Promise<T>,
   { slowTolerance: tooLong }: Options = { slowTolerance: 1000 },
@@ -42,11 +53,15 @@ export function useAsync<T>(
   const [isLoading, setLoading] = useState<boolean>(false);
   const [error, setError] = useState<any>(undefined);
   const [isSlow, setSlow] = useState(false);
+  const isMounted = useIsMounted();
 
   const request = async (...args: any) => {
     if (!fn) return;
     setLoading(true);
     const handler = setTimeout(() => {
+      if (!isMounted()) {
+        return;
+      }
       setSlow(true);
     }, tooLong);
 
@@ -54,6 +69,10 @@ export function useAsync<T>(
       console.log("calling async", args);
       const result = await fn(...args);
       console.log("async back", result);
+      if (!isMounted()) {
+        // Possibly calling fn(...) resulted in the component being unmounted.
+        return;
+      }
       setData(result);
     } catch (error) {
       setError(error);
diff --git a/packages/taler-util/src/taler-error-codes.ts 
b/packages/taler-util/src/taler-error-codes.ts
index 8ea97f7e..53928986 100644
--- a/packages/taler-util/src/taler-error-codes.ts
+++ b/packages/taler-util/src/taler-error-codes.ts
@@ -220,6 +220,13 @@ export enum TalerErrorCode {
    */
   GENERIC_FAILED_COMPUTE_JSON_HASH = 61,
 
+  /**
+   * The service could not compute an amount.
+   * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  GENERIC_FAILED_COMPUTE_AMOUNT = 62,
+
   /**
    * The HTTP server had insufficient memory to parse the request.
    * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
@@ -395,6 +402,20 @@ export enum TalerErrorCode {
    */
   EXCHANGE_GENERIC_CLOCK_SKEW = 1020,
 
+  /**
+   * The specified amount for the coin is higher than the value of the 
denomination of the coin.
+   * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE = 1021,
+
+  /**
+   * The exchange was not properly configured with global fees.
+   * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_GENERIC_GLOBAL_FEES_MISSING = 1022,
+
   /**
    * The exchange did not find information about the specified transaction in 
the database.
    * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
@@ -1081,6 +1102,83 @@ export enum TalerErrorCode {
    */
   EXCHANGE_MANAGEMENT_GLOBAL_FEE_SIGNATURE_INVALID = 1817,
 
+  /**
+   * The purse was previously created with different meta data.
+   * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA = 1850,
+
+  /**
+   * The purse was previously created with a different contract.
+   * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_CREATE_CONFLICTING_CONTRACT_STORED = 1851,
+
+  /**
+   * A coin signature for a deposit into the purse is invalid.
+   * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_CREATE_COIN_SIGNATURE_INVALID = 1852,
+
+  /**
+   * The purse expiration time is in the past.
+   * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_CREATE_EXPIRATION_BEFORE_NOW = 1853,
+
+  /**
+   * The purse expiration time is "never".
+   * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_CREATE_EXPIRATION_IS_NEVER = 1854,
+
+  /**
+   * The purse signature over the purse meta data is invalid.
+   * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_CREATE_SIGNATURE_INVALID = 1855,
+
+  /**
+   * The signature over the encrypted contract is invalid.
+   * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_ECONTRACT_SIGNATURE_INVALID = 1856,
+
+  /**
+   * The signature from the exchange over the confirmation is invalid.
+   * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID = 1857,
+
+  /**
+   * The coin was previously deposited with different meta data.
+   * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA = 1858,
+
+  /**
+   * The encrypted contract was previously uploaded with different meta data.
+   * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA = 1859,
+
+  /**
+   * The deposited amount is less than the purse fee.
+   * Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_CREATE_PURSE_NEGATIVE_VALUE_AFTER_FEE = 1860,
+
   /**
    * The auditor signature over the denomination meta data is invalid.
    * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).

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