gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (43f557277 -> 7e37b3474)


From: gnunet
Subject: [taler-wallet-core] branch master updated (43f557277 -> 7e37b3474)
Date: Fri, 21 Jul 2023 20:51:00 +0200

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

sebasjm pushed a change to branch master
in repository wallet-core.

    from 43f557277 empty list when loading
     new e90f1b420 move amount and time function to util
     new 7e37b3474 case details and missing decision encryption

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 packages/aml-backoffice-ui/src/account.ts          |  19 ++-
 .../src/handlers/FormProvider.tsx                  |   2 +-
 packages/aml-backoffice-ui/src/hooks/useBackend.ts |   8 +-
 .../aml-backoffice-ui/src/hooks/useCaseDetails.ts  | 162 +++++++++++++++++++++
 packages/aml-backoffice-ui/src/hooks/useCases.ts   |  53 +++++++
 packages/aml-backoffice-ui/src/pages.ts            |   2 +-
 .../aml-backoffice-ui/src/pages/CaseDetails.tsx    | 131 +++++++----------
 packages/aml-backoffice-ui/src/pages/Cases.tsx     |  53 -------
 .../aml-backoffice-ui/src/pages/NewFormEntry.tsx   |  40 ++++-
 packages/aml-backoffice-ui/src/types.ts            |  36 +++++
 packages/taler-util/src/taler-crypto.ts            |  61 ++++++++
 .../src/crypto/cryptoImplementation.ts             |  61 +-------
 12 files changed, 426 insertions(+), 202 deletions(-)
 create mode 100644 packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts

diff --git a/packages/aml-backoffice-ui/src/account.ts 
b/packages/aml-backoffice-ui/src/account.ts
index 1c8cd7f53..2225bf2ff 100644
--- a/packages/aml-backoffice-ui/src/account.ts
+++ b/packages/aml-backoffice-ui/src/account.ts
@@ -1,6 +1,7 @@
 import {
-  PaytoUri,
+  Amounts,
   TalerSignaturePurpose,
+  amountToBuffer,
   bufferForUint32,
   buildSigPS,
   createEddsaKeyPair,
@@ -11,8 +12,10 @@ import {
   encodeCrock,
   encryptWithDerivedKey,
   getRandomBytesF,
+  hash,
+  hashTruncate32,
   stringToBytes,
-  stringifyPaytoUri,
+  timestampRoundedToBuffer
 } from "@gnu-taler/taler-util";
 import { AmlExchangeBackend } from "./types.js";
 
@@ -60,12 +63,16 @@ export function buildQuerySignature(key: SigningKey): 
string {
 }
 export function buildDecisionSignature(
   key: SigningKey,
-  payto: PaytoUri,
-  state: AmlExchangeBackend.AmlState,
+  decision: AmlExchangeBackend.AmlDecision,
 ): string {
+
   const sigBlob = 
buildSigPS(TalerSignaturePurpose.TALER_SIGNATURE_AML_DECISION)
-    .put(decodeCrock(stringifyPaytoUri(payto)))
-    .put(bufferForUint32(state))
+    .put(hash(stringToBytes(decision.justification)))
+    // .put(timestampRoundedToBuffer(decision.decision_time))
+    .put(amountToBuffer(decision.new_threshold))
+    .put(decodeCrock(decision.h_payto))
+    // .put(hash(stringToBytes(decision.kyc_requirements)))
+    .put(bufferForUint32(decision.new_state))
     .build();
 
   return encodeCrock(eddsaSign(sigBlob, key));
diff --git a/packages/aml-backoffice-ui/src/handlers/FormProvider.tsx 
b/packages/aml-backoffice-ui/src/handlers/FormProvider.tsx
index a195c2051..3da2a4f07 100644
--- a/packages/aml-backoffice-ui/src/handlers/FormProvider.tsx
+++ b/packages/aml-backoffice-ui/src/handlers/FormProvider.tsx
@@ -29,7 +29,7 @@ export type FormState<T> = {
     ? Partial<InputFieldState>
     : T[field] extends Array<infer P>
     ? Partial<InputArrayFieldState<P>>
-    : T[field] extends object
+    : T[field] extends (object | undefined)
     ? FormState<T[field]>
     : Partial<InputFieldState>;
 };
diff --git a/packages/aml-backoffice-ui/src/hooks/useBackend.ts 
b/packages/aml-backoffice-ui/src/hooks/useBackend.ts
index e68efb2e3..b9d66fca6 100644
--- a/packages/aml-backoffice-ui/src/hooks/useBackend.ts
+++ b/packages/aml-backoffice-ui/src/hooks/useBackend.ts
@@ -14,7 +14,7 @@ interface useBackendType {
     path: string,
     options?: RequestOptions,
   ) => Promise<HttpResponseOk<T>>;
-  fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
+  fetcher: <T>(args: [string, string]) => Promise<HttpResponseOk<T>>;
   paginatedFetcher: <T>(
     args: [string, number, number, string],
   ) => Promise<HttpResponseOk<T>>;
@@ -35,8 +35,10 @@ export function usePublicBackend(): useBackendType {
   );
 
   const fetcher = useCallback(
-    function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint);
+    function fetcherImpl<T>([endpoint, talerAmlOfficerSignature]: 
[string,string]): Promise<HttpResponseOk<T>> {
+      return requestHandler<T>(baseUrl, endpoint, {
+        talerAmlOfficerSignature
+      });
     },
     [baseUrl],
   );
diff --git a/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts 
b/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts
new file mode 100644
index 000000000..980a35f21
--- /dev/null
+++ b/packages/aml-backoffice-ui/src/hooks/useCaseDetails.ts
@@ -0,0 +1,162 @@
+
+import {
+  HttpResponse,
+  HttpResponseOk,
+  RequestError
+} from "@gnu-taler/web-util/browser";
+import { AmlExchangeBackend } from "../types.js";
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook, useSWRConfig } from "swr";
+import { AccountId } from "../account.js";
+import { usePublicBackend } from "./useBackend.js";
+const useSWR = _useSWR as unknown as SWRHook;
+
+export function useCaseDetails(
+  account: AccountId,
+  paytoHash: string,
+  signature: string | undefined,
+): HttpResponse<
+  AmlExchangeBackend.AmlDecisionDetails,
+  AmlExchangeBackend.AmlError
+> {
+  const { fetcher } = usePublicBackend();
+
+  const { data, error } = useSWR<
+  HttpResponseOk<AmlExchangeBackend.AmlDecisionDetails>,
+  RequestError<AmlExchangeBackend.AmlError>
+>(    [
+  `aml/${account}/decision/${(paytoHash)}`,
+  signature,
+],
+fetcher, {
+    refreshInterval: 0,
+    refreshWhenHidden: false,
+    revalidateOnFocus: false,
+    revalidateOnReconnect: false,
+    refreshWhenOffline: false,
+    errorRetryCount: 0,
+    errorRetryInterval: 1,
+    shouldRetryOnError: false,
+    keepPreviousData: true,
+  });
+
+  if (data) return data;
+  if (error) return error.cause;
+  return { loading: true };
+}
+
+const example1: AmlExchangeBackend.AmlDecisionDetails = {
+  aml_history: [
+    {
+      justification: "Lack of documentation",
+      decider_pub: "ASDASDASD",
+      decision_time: {
+        t_s: Date.now() / 1000,
+      },
+      new_state: 2,
+      new_threshold: "USD:0",
+    },
+    {
+      justification: "Doing a transfer of high amount",
+      decider_pub: "ASDASDASD",
+      decision_time: {
+        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 6,
+      },
+      new_state: 1,
+      new_threshold: "USD:2000",
+    },
+    {
+      justification: "Account is known to the system",
+      decider_pub: "ASDASDASD",
+      decision_time: {
+        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 9,
+      },
+      new_state: 0,
+      new_threshold: "USD:100",
+    },
+  ],
+  kyc_attributes: [
+    {
+      collection_time: {
+        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 8,
+      },
+      expiration_time: {
+        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 4,
+      },
+      provider_section: "asdasd",
+      attributes: {
+        name: "Sebastian",
+      },
+    },
+    {
+      collection_time: {
+        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 5,
+      },
+      expiration_time: {
+        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 2,
+      },
+      provider_section: "asdasd",
+      attributes: {
+        creditCard: "12312312312",
+      },
+    },
+  ],
+};
+
+export const exampleResponse: 
HttpResponse<AmlExchangeBackend.AmlDecisionDetails,AmlExchangeBackend.AmlError> 
= {
+  ok: true,
+  data: example1,
+}
+
+
+export function useAmlCasesAPI(): AmlCaseAPI {
+  const { request } = usePublicBackend();
+  const mutateAll = useMatchMutate();
+
+  const updateDecision = async (
+    officer: AccountId,
+    data: AmlExchangeBackend.AmlDecision,
+  ): Promise<HttpResponseOk<void>> => {
+    const res = await request<void>(`aml/${officer}/decision`, {
+      method: "POST",
+      data,
+      contentType: "json",
+    });
+    await mutateAll(/.*aml.*/);
+    return res;
+  };
+
+  return {
+    updateDecision,
+  };
+}
+
+export interface AmlCaseAPI {
+  updateDecision: (
+    officer: AccountId,
+    data: AmlExchangeBackend.AmlDecision,
+  ) => Promise<HttpResponseOk<void>>;
+}
+
+
+function useMatchMutate(): (
+  re: RegExp,
+  value?: unknown,
+) => Promise<any> {
+  const { cache, mutate } = useSWRConfig();
+
+  if (!(cache instanceof Map)) {
+    throw new Error(
+      "matchMutate requires the cache provider to be a Map instance",
+    );
+  }
+
+  return function matchRegexMutate(re: RegExp, value?: unknown) {
+    const allKeys = Array.from(cache.keys());
+    const keys = allKeys.filter((key) => re.test(key));
+    const mutations = keys.map((key) => {
+      return mutate(key, value, true);
+    });
+    return Promise.all(mutations);
+  };
+}
diff --git a/packages/aml-backoffice-ui/src/hooks/useCases.ts 
b/packages/aml-backoffice-ui/src/hooks/useCases.ts
index 04b7c383b..c5a0fc489 100644
--- a/packages/aml-backoffice-ui/src/hooks/useCases.ts
+++ b/packages/aml-backoffice-ui/src/hooks/useCases.ts
@@ -92,3 +92,56 @@ export function useCases(
   }
   return { loading: true };
 }
+
+const example1: AmlExchangeBackend.AmlRecords = {
+  records: [
+    {
+      current_state: 0,
+      h_payto: "QWEQWEQWEQWEWQE",
+      rowid: 1,
+      threshold: "USD 100",
+    },
+    {
+      current_state: 1,
+      h_payto: "ASDASDASD",
+      rowid: 1,
+      threshold: "USD 100",
+    },
+    {
+      current_state: 2,
+      h_payto: "ZXCZXCZXCXZC",
+      rowid: 1,
+      threshold: "USD 1000",
+    },
+    {
+      current_state: 0,
+      h_payto: "QWEQWEQWEQWEWQE",
+      rowid: 1,
+      threshold: "USD 100",
+    },
+    {
+      current_state: 1,
+      h_payto: "ASDASDASD",
+      rowid: 1,
+      threshold: "USD 100",
+    },
+    {
+      current_state: 2,
+      h_payto: "ZXCZXCZXCXZC",
+      rowid: 1,
+      threshold: "USD 1000",
+    },
+  ].map((e, idx) => {
+    e.rowid = idx;
+    e.threshold = `${e.threshold}${idx}`;
+    return e;
+  }),
+};
+
+export const exampleResponse: 
HttpResponsePaginated<AmlExchangeBackend.AmlRecords,AmlExchangeBackend.AmlError>
 = {
+  ok: true,
+  data: example1,
+  loadMore: () => {},
+  loadMorePrev: () => {},  
+}
+
diff --git a/packages/aml-backoffice-ui/src/pages.ts 
b/packages/aml-backoffice-ui/src/pages.ts
index 18fb7a158..e4e16f05f 100644
--- a/packages/aml-backoffice-ui/src/pages.ts
+++ b/packages/aml-backoffice-ui/src/pages.ts
@@ -16,7 +16,7 @@ const cases: PageEntry = {
   url: "#/cases",
   view: Cases,
 };
-const account: PageEntry<{ account?: string }> = {
+const account: PageEntry<{ account: string }> = {
   url: pageDefinition("#/account/:account"),
   view: CaseDetails,
 };
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx 
b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index d02d8b395..ce820d612 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -13,64 +13,13 @@ import { FlexibleForm } from "../forms/index.js";
 import { UIFormField } from "../handlers/forms.js";
 import { Pages } from "../pages.js";
 import { AmlExchangeBackend } from "../types.js";
+import { HandleAccountNotReady } from "./HandleAccountNotReady.js";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { useOfficer } from "../hooks/useOfficer.js";
+import { buildQuerySignature } from "../account.js";
+import { useCaseDetails } from "../hooks/useCaseDetails.js";
+import { handleNotOkResult } from "../utils/errors.js";
 
-const response: AmlExchangeBackend.AmlDecisionDetails = {
-  aml_history: [
-    {
-      justification: "Lack of documentation",
-      decider_pub: "ASDASDASD",
-      decision_time: {
-        t_s: Date.now() / 1000,
-      },
-      new_state: 2,
-      new_threshold: "USD:0",
-    },
-    {
-      justification: "Doing a transfer of high amount",
-      decider_pub: "ASDASDASD",
-      decision_time: {
-        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 6,
-      },
-      new_state: 1,
-      new_threshold: "USD:2000",
-    },
-    {
-      justification: "Account is known to the system",
-      decider_pub: "ASDASDASD",
-      decision_time: {
-        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 9,
-      },
-      new_state: 0,
-      new_threshold: "USD:100",
-    },
-  ],
-  kyc_attributes: [
-    {
-      collection_time: {
-        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 8,
-      },
-      expiration_time: {
-        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 4,
-      },
-      provider_section: "asdasd",
-      attributes: {
-        name: "Sebastian",
-      },
-    },
-    {
-      collection_time: {
-        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 5,
-      },
-      expiration_time: {
-        t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 2,
-      },
-      provider_section: "asdasd",
-      attributes: {
-        creditCard: "12312312312",
-      },
-    },
-  ],
-};
 type AmlEvent = AmlFormEvent | KycCollectionEvent | KycExpirationEvent;
 type AmlFormEvent = {
   type: "aml-form";
@@ -127,7 +76,7 @@ function getEventsFromAmlHistory(
     });
     prev.push({
       type: "kyc-expiration",
-      title: "expired" as TranslatedString,
+      title: "expiration" as TranslatedString,
       when: AbsoluteTime.fromProtocolTimestamp(k.expiration_time),
       fields: !k.attributes ? [] : Object.keys(k.attributes),
     });
@@ -136,19 +85,30 @@ function getEventsFromAmlHistory(
   return ae.concat(ke).sort(selectSooner);
 }
 
-export function CaseDetails({ account }: { account?: string }) {
-  const events = getEventsFromAmlHistory(
-    response.aml_history,
-    response.kyc_attributes,
-  );
-  console.log("DETAILS", events, events[events.length - 1 - 2]);
-  const [selected, setSelected] = useState<AmlEvent>(
-    events[events.length - 1 - 2],
-  );
+export function CaseDetails({ account: paytoHash }: { account: string }) {
+  const [selected, setSelected] = useState<AmlEvent | undefined>(undefined);
+
+  const officer = useOfficer();
+  const { i18n } = useTranslationContext();
+  if (officer.state !== "ready") {
+    return <HandleAccountNotReady officer={officer} />;
+  }
+  const signature =
+    officer.state === "ready"
+      ? buildQuerySignature(officer.account.signingKey)
+      : undefined;
+  const details = useCaseDetails(officer.account.accountId, paytoHash, 
signature)
+  if (!details.ok && !details.loading) {
+    return handleNotOkResult(i18n)(details);
+  }
+  const aml_history = details.loading ? [] : details.data.aml_history
+  const kyc_attributes = details.loading ? [] : details.data.kyc_attributes
+  const events = getEventsFromAmlHistory(aml_history,kyc_attributes);
+  
   return (
     <div>
       <a
-        href={Pages.newFormEntry.url({ account })}
+        href={Pages.newFormEntry.url({ account: paytoHash })}
         class="m-4 block rounded-md w-fit border-0 px-3 py-2 text-center 
text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700"
       >
         New AML form
@@ -271,13 +231,24 @@ function ShowConsolidated({
   history: AmlEvent[];
   until: AmlEvent;
 }): VNode {
-  console.log("UNTIL", until);
   const cons = getConsolidated(history, until.when);
 
   const form: FlexibleForm<Consolidated> = {
     versionId: "1",
     behavior: (form) => {
-      return {};
+      return {
+        aml: {
+          threshold: {
+            hidden: !form.aml
+          },
+          since: {
+            hidden: !form.aml
+          },
+          state: {
+            hidden: !form.aml
+          }
+        }
+      };
     },
     design: [
       {
@@ -356,8 +327,8 @@ function ShowConsolidated({
 
 interface Consolidated {
   aml: {
-    state?: AmlExchangeBackend.AmlState;
-    threshold?: AmountJson;
+    state: AmlExchangeBackend.AmlState;
+    threshold: AmountJson;
     since: AbsoluteTime;
   };
   kyc: {
@@ -375,7 +346,13 @@ function getConsolidated(
 ): Consolidated {
   const initial: Consolidated = {
     aml: {
-      since: AbsoluteTime.never(),
+      state: AmlExchangeBackend.AmlState.normal,
+      threshold: {
+        currency: "ARS",
+        value: 1000,
+        fraction: 0,
+      },
+      since: AbsoluteTime.never()
     },
     kyc: {},
   };
@@ -391,9 +368,11 @@ function getConsolidated(
         break;
       }
       case "aml-form": {
-        prev.aml.threshold = cur.threshold;
-        prev.aml.state = cur.state;
-        prev.aml.since = cur.when;
+        prev.aml = {
+          since: cur.when,
+          state: cur.state,
+          threshold: cur.threshold
+        }
         break;
       }
       case "kyc-collection": {
diff --git a/packages/aml-backoffice-ui/src/pages/Cases.tsx 
b/packages/aml-backoffice-ui/src/pages/Cases.tsx
index 8b115ed7e..990c0d2d4 100644
--- a/packages/aml-backoffice-ui/src/pages/Cases.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Cases.tsx
@@ -12,59 +12,6 @@ import { buildQuerySignature } from "../account.js";
 import { handleNotOkResult } from "../utils/errors.js";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 
-const response: AmlExchangeBackend.AmlRecords = {
-  records: [
-    {
-      current_state: 0,
-      h_payto: "QWEQWEQWEQWEWQE",
-      rowid: 1,
-      threshold: "USD 100",
-    },
-    {
-      current_state: 1,
-      h_payto: "ASDASDASD",
-      rowid: 1,
-      threshold: "USD 100",
-    },
-    {
-      current_state: 2,
-      h_payto: "ZXCZXCZXCXZC",
-      rowid: 1,
-      threshold: "USD 1000",
-    },
-    {
-      current_state: 0,
-      h_payto: "QWEQWEQWEQWEWQE",
-      rowid: 1,
-      threshold: "USD 100",
-    },
-    {
-      current_state: 1,
-      h_payto: "ASDASDASD",
-      rowid: 1,
-      threshold: "USD 100",
-    },
-    {
-      current_state: 2,
-      h_payto: "ZXCZXCZXCXZC",
-      rowid: 1,
-      threshold: "USD 1000",
-    },
-  ].map((e, idx) => {
-    e.rowid = idx;
-    e.threshold = `${e.threshold}${idx}`;
-    return e;
-  }),
-};
-
-function doFilter(
-  list: typeof response.records,
-  filter: AmlExchangeBackend.AmlState | undefined,
-): typeof response.records {
-  if (filter === undefined) return list;
-  return list.filter((r) => r.current_state === filter);
-}
-
 export function Cases() {
   const officer = useOfficer();
   const { i18n } = useTranslationContext();
diff --git a/packages/aml-backoffice-ui/src/pages/NewFormEntry.tsx 
b/packages/aml-backoffice-ui/src/pages/NewFormEntry.tsx
index bbd04daee..13e78b169 100644
--- a/packages/aml-backoffice-ui/src/pages/NewFormEntry.tsx
+++ b/packages/aml-backoffice-ui/src/pages/NewFormEntry.tsx
@@ -2,8 +2,12 @@ import { VNode, h } from "preact";
 import { allForms } from "./AntiMoneyLaunderingForm.js";
 import { Pages } from "../pages.js";
 import { NiceForm } from "../NiceForm.js";
-import { AbsoluteTime, Amounts } from "@gnu-taler/taler-util";
+import { AbsoluteTime, Amounts, TalerProtocolTimestamp } from 
"@gnu-taler/taler-util";
 import { AmlExchangeBackend } from "../types.js";
+import { useAmlCasesAPI } from "../hooks/useCaseDetails.js";
+import { useOfficer } from "../hooks/useOfficer.js";
+import { HandleAccountNotReady } from "./HandleAccountNotReady.js";
+import { buildDecisionSignature, buildQuerySignature } from "../account.js";
 
 export function NewFormEntry({
   account,
@@ -12,30 +16,58 @@ export function NewFormEntry({
   account?: string;
   type?: string;
 }): VNode {
+  const officer = useOfficer();
+
   if (!account) {
     return <div>no account</div>;
   }
   if (!type) {
     return <SelectForm account={account} />;
   }
+  if (officer.state !== "ready") {
+    return <HandleAccountNotReady officer={officer} />;
+  }
 
   const selectedForm = Number.parseInt(type ?? "0", 10);
   if (Number.isNaN(selectedForm)) {
     return <div>WHAT! {type}</div>;
   }
   const showingFrom = allForms[selectedForm].impl;
+  const formName = allForms[selectedForm].name
   const initial = {
     fullName: "loggedIn_user_fullname",
     when: AbsoluteTime.now(),
     state: AmlExchangeBackend.AmlState.pending,
-    threshold: Amounts.parseOrThrow("USD:10"),
+    threshold: Amounts.parseOrThrow("ARS:1000"),
   };
+  const api = useAmlCasesAPI()
+  
   return (
     <NiceForm
       initial={initial}
       form={showingFrom(initial)}
-      onSubmit={(v) => {
-        alert(JSON.stringify(v));
+      onSubmit={(formValue) => {
+        if (formValue.state === undefined || formValue.threshold === 
undefined) return;
+        
+        const justification = {
+          index: selectedForm,
+          name: formName,
+          value: formValue
+        }
+        const decision: AmlExchangeBackend.AmlDecision = {
+          justification: JSON.stringify(justification),
+          decision_time: TalerProtocolTimestamp.now(),
+          h_payto: account,
+          new_state: formValue.state,
+          new_threshold: Amounts.stringify(formValue.threshold),
+          officer_sig: "",
+          kyc_requirements: undefined
+        }
+        const signature = buildDecisionSignature(officer.account.signingKey, 
decision);
+        decision.officer_sig = signature
+        api.updateDecision(officer.account.accountId, decision);
+
+        // alert(JSON.stringify(formValue));
       }}
     >
       <div class="mt-6 flex items-center justify-end gap-x-6">
diff --git a/packages/aml-backoffice-ui/src/types.ts 
b/packages/aml-backoffice-ui/src/types.ts
index 104d938b3..429b538e7 100644
--- a/packages/aml-backoffice-ui/src/types.ts
+++ b/packages/aml-backoffice-ui/src/types.ts
@@ -59,6 +59,9 @@ export namespace AmlExchangeBackend {
   type PaytoHash = string;
   type Integer = number;
   type Amount = string;
+  // EdDSA signatures are transmitted as 64-bytes base32
+  // binary-encoded objects with just the R and S values (base32_ binary-only).
+  type EddsaSignature = string;
 
   export interface AmlRecords {
     // Array of AML records matching the query.
@@ -85,4 +88,37 @@ export namespace AmlExchangeBackend {
     pending = 1,
     frozen = 2,
   }
+
+  
+  export interface AmlDecision {
+
+    // Human-readable justification for the decision.
+    justification: string;
+  
+    // At what monthly transaction volume should the
+    // decision be automatically reviewed?
+    new_threshold: Amount;
+  
+    // Which payto-address is the decision about?
+    // Identifies a GNU Taler wallet or an affected bank account.
+    h_payto: PaytoHash;
+  
+    // What is the new AML state (e.g. frozen, unfrozen, etc.)
+    // Numerical values are defined in AmlDecisionState.
+    new_state: Integer;
+  
+    // Signature by the AML officer over a
+    // TALER_MasterAmlOfficerStatusPS.
+    // Must have purpose TALER_SIGNATURE_MASTER_AML_KEY.
+    officer_sig: EddsaSignature;
+  
+    // When was the decision made?
+    decision_time: Timestamp;
+  
+    // Optional argument to impose new KYC requirements
+    // that the customer has to satisfy to unblock transactions.
+    kyc_requirements?: string[];
+  }
+  
+
 }
diff --git a/packages/taler-util/src/taler-crypto.ts 
b/packages/taler-util/src/taler-crypto.ts
index dd8d7dd7b..e3f7d49a8 100644
--- a/packages/taler-util/src/taler-crypto.ts
+++ b/packages/taler-util/src/taler-crypto.ts
@@ -35,6 +35,8 @@ import { Logger } from "./logging.js";
 import { secretbox } from "./nacl-fast.js";
 import * as fflate from "fflate";
 import { canonicalJson } from "./helpers.js";
+import { TalerProtocolDuration, TalerProtocolTimestamp } from "./time.js";
+import { AmountLike, Amounts } from "./amounts.js";
 
 export type Flavor<T, FlavorT extends string> = T & {
   _flavor?: `taler.${FlavorT}`;
@@ -963,6 +965,7 @@ export enum TalerSignaturePurpose {
   EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041,
   TALER_SIGNATURE_AML_DECISION = 1350,
   TALER_SIGNATURE_AML_QUERY = 1351,
+  TALER_SIGNATURE_MASTER_AML_KEY = 1017,
   ANASTASIS_POLICY_UPLOAD = 1400,
   ANASTASIS_POLICY_DOWNLOAD = 1401,
   SYNC_BACKUP_UPLOAD = 1450,
@@ -1546,3 +1549,61 @@ export async function decryptContractForDeposit(
     contractTerms: JSON.parse(contractTermsString),
   };
 }
+
+export function amountToBuffer(amount: AmountLike): Uint8Array {
+  const amountJ = Amounts.jsonifyAmount(amount);
+  const buffer = new ArrayBuffer(8 + 4 + 12);
+  const dvbuf = new DataView(buffer);
+  const u8buf = new Uint8Array(buffer);
+  const curr = stringToBytes(amountJ.currency);
+  if (typeof dvbuf.setBigUint64 !== "undefined") {
+    dvbuf.setBigUint64(0, BigInt(amountJ.value));
+  } else {
+    const arr = bigint(amountJ.value).toArray(2 ** 8).value;
+    let offset = 8 - arr.length;
+    for (let i = 0; i < arr.length; i++) {
+      dvbuf.setUint8(offset++, arr[i]);
+    }
+  }
+  dvbuf.setUint32(8, amountJ.fraction);
+  u8buf.set(curr, 8 + 4);
+
+  return u8buf;
+}
+
+export function timestampRoundedToBuffer(ts: TalerProtocolTimestamp): 
Uint8Array {
+  const b = new ArrayBuffer(8);
+  const v = new DataView(b);
+  // The buffer we sign over represents the timestamp in microseconds.
+  if (typeof v.setBigUint64 !== "undefined") {
+    const s = BigInt(ts.t_s) * BigInt(1000 * 1000);
+    v.setBigUint64(0, s);
+  } else {
+    const s =
+      ts.t_s === "never" ? bigint.zero : bigint(ts.t_s).multiply(1000 * 1000);
+    const arr = s.toArray(2 ** 8).value;
+    let offset = 8 - arr.length;
+    for (let i = 0; i < arr.length; i++) {
+      v.setUint8(offset++, arr[i]);
+    }
+  }
+  return new Uint8Array(b);
+}
+
+export function durationRoundedToBuffer(ts: TalerProtocolDuration): Uint8Array 
{
+  const b = new ArrayBuffer(8);
+  const v = new DataView(b);
+  // The buffer we sign over represents the timestamp in microseconds.
+  if (typeof v.setBigUint64 !== "undefined") {
+    const s = BigInt(ts.d_us);
+    v.setBigUint64(0, s);
+  } else {
+    const s = ts.d_us === "forever" ? bigint.zero : bigint(ts.d_us);
+    const arr = s.toArray(2 ** 8).value;
+    let offset = 8 - arr.length;
+    for (let i = 0; i < arr.length; i++) {
+      v.setUint8(offset++, arr[i]);
+    }
+  }
+  return new Uint8Array(b);
+}
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts 
b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index 1dd70304a..76c13bcb4 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -31,6 +31,7 @@ import {
   AmountLike,
   Amounts,
   AmountString,
+  amountToBuffer,
   BlindedDenominationSignature,
   bufferForUint32,
   bufferForUint64,
@@ -44,6 +45,7 @@ import {
   decryptContractForMerge,
   DenomKeyType,
   DepositInfo,
+  durationRoundedToBuffer,
   ecdhGetPublic,
   eddsaGetPublic,
   EddsaPublicKeyString,
@@ -82,6 +84,7 @@ import {
   TalerProtocolDuration,
   TalerProtocolTimestamp,
   TalerSignaturePurpose,
+  timestampRoundedToBuffer,
   UnblindedSignature,
   validateIban,
   WireFee,
@@ -1698,64 +1701,6 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
   },
 };
 
-function amountToBuffer(amount: AmountLike): Uint8Array {
-  const amountJ = Amounts.jsonifyAmount(amount);
-  const buffer = new ArrayBuffer(8 + 4 + 12);
-  const dvbuf = new DataView(buffer);
-  const u8buf = new Uint8Array(buffer);
-  const curr = stringToBytes(amountJ.currency);
-  if (typeof dvbuf.setBigUint64 !== "undefined") {
-    dvbuf.setBigUint64(0, BigInt(amountJ.value));
-  } else {
-    const arr = bigint(amountJ.value).toArray(2 ** 8).value;
-    let offset = 8 - arr.length;
-    for (let i = 0; i < arr.length; i++) {
-      dvbuf.setUint8(offset++, arr[i]);
-    }
-  }
-  dvbuf.setUint32(8, amountJ.fraction);
-  u8buf.set(curr, 8 + 4);
-
-  return u8buf;
-}
-
-function timestampRoundedToBuffer(ts: TalerProtocolTimestamp): Uint8Array {
-  const b = new ArrayBuffer(8);
-  const v = new DataView(b);
-  // The buffer we sign over represents the timestamp in microseconds.
-  if (typeof v.setBigUint64 !== "undefined") {
-    const s = BigInt(ts.t_s) * BigInt(1000 * 1000);
-    v.setBigUint64(0, s);
-  } else {
-    const s =
-      ts.t_s === "never" ? bigint.zero : bigint(ts.t_s).multiply(1000 * 1000);
-    const arr = s.toArray(2 ** 8).value;
-    let offset = 8 - arr.length;
-    for (let i = 0; i < arr.length; i++) {
-      v.setUint8(offset++, arr[i]);
-    }
-  }
-  return new Uint8Array(b);
-}
-
-function durationRoundedToBuffer(ts: TalerProtocolDuration): Uint8Array {
-  const b = new ArrayBuffer(8);
-  const v = new DataView(b);
-  // The buffer we sign over represents the timestamp in microseconds.
-  if (typeof v.setBigUint64 !== "undefined") {
-    const s = BigInt(ts.d_us);
-    v.setBigUint64(0, s);
-  } else {
-    const s = ts.d_us === "forever" ? bigint.zero : bigint(ts.d_us);
-    const arr = s.toArray(2 ** 8).value;
-    let offset = 8 - arr.length;
-    for (let i = 0; i < arr.length; i++) {
-      v.setUint8(offset++, arr[i]);
-    }
-  }
-  return new Uint8Array(b);
-}
-
 export interface EddsaSignRequest {
   msg: string;
   priv: string;

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