aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-core')
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoImplementation.ts16
-rw-r--r--packages/taler-wallet-core/src/db.ts2
-rw-r--r--packages/taler-wallet-core/src/deposits.ts109
-rw-r--r--packages/taler-wallet-core/src/pay-merchant.ts4
-rw-r--r--packages/taler-wallet-core/src/pay-peer-push-credit.ts3
-rw-r--r--packages/taler-wallet-core/src/withdraw.ts8
6 files changed, 117 insertions, 25 deletions
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index 69081d234..ce6193b83 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -170,7 +170,7 @@ export interface TalerCryptoInterface {
req: ContractTermsValidationRequest,
): Promise<ValidationResult>;
- createEddsaKeypair(req: {}): Promise<EddsaKeypair>;
+ createEddsaKeypair(req: {}): Promise<EddsaKeypairStrings>;
eddsaGetPublic(req: EddsaGetPublicRequest): Promise<EddsaGetPublicResponse>;
@@ -333,10 +333,12 @@ export const nullCrypto: TalerCryptoInterface = {
): Promise<ValidationResult> {
throw new Error("Function not implemented.");
},
- createEddsaKeypair: function (req: unknown): Promise<EddsaKeypair> {
+ createEddsaKeypair: function (req: unknown): Promise<EddsaKeypairStrings> {
throw new Error("Function not implemented.");
},
- eddsaGetPublic: function (req: EddsaGetPublicRequest): Promise<EddsaKeypair> {
+ eddsaGetPublic: function (
+ req: EddsaGetPublicRequest,
+ ): Promise<EddsaKeypairStrings> {
throw new Error("Function not implemented.");
},
unblindDenominationSignature: function (
@@ -600,7 +602,7 @@ export interface WireAccountValidationRequest {
creditRestrictions?: any[];
}
-export interface EddsaKeypair {
+export interface EddsaKeypairStrings {
priv: string;
pub: string;
}
@@ -1081,7 +1083,9 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
/**
* Create a new EdDSA key pair.
*/
- async createEddsaKeypair(tci: TalerCryptoInterfaceR): Promise<EddsaKeypair> {
+ async createEddsaKeypair(
+ tci: TalerCryptoInterfaceR,
+ ): Promise<EddsaKeypairStrings> {
const eddsaPriv = encodeCrock(getRandomBytes(32));
const eddsaPubRes = await tci.eddsaGetPublic(tci, {
priv: eddsaPriv,
@@ -1095,7 +1099,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
async eddsaGetPublic(
tci: TalerCryptoInterfaceR,
req: EddsaGetPublicRequest,
- ): Promise<EddsaKeypair> {
+ ): Promise<EddsaKeypairStrings> {
return {
priv: req.priv,
pub: encodeCrock(eddsaGetPublic(decodeCrock(req.priv))),
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 21ec3f033..99b69135a 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1831,7 +1831,7 @@ export interface DepositGroupRecord {
}
export interface DepositKycInfo {
- accessToken: string;
+ accessToken?: string;
paytoHash: string;
exchangeBaseUrl: string;
}
diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts
index 34b19df80..da69530f7 100644
--- a/packages/taler-wallet-core/src/deposits.ts
+++ b/packages/taler-wallet-core/src/deposits.ts
@@ -61,6 +61,7 @@ import {
canonicalJson,
checkDbInvariant,
checkLogicInvariant,
+ codecForAccountKycStatus,
codecForBatchDepositSuccess,
codecForTackTransactionAccepted,
codecForTackTransactionWired,
@@ -72,7 +73,12 @@ import {
parsePaytoUri,
stringToBytes,
} from "@gnu-taler/taler-util";
-import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
+import {
+ readResponseJsonOrThrow,
+ readSuccessResponseJsonOrThrow,
+ readTalerErrorResponse,
+ throwUnexpectedRequestError,
+} from "@gnu-taler/taler-util/http";
import { selectPayCoins, selectPayCoinsInTx } from "./coinSelection.js";
import {
PendingTaskType,
@@ -94,6 +100,7 @@ import {
RefreshOperationStatus,
WalletDbAllStoresReadOnlyTransaction,
WalletDbReadWriteTransaction,
+ timestampAbsoluteFromDb,
timestampPreciseFromDb,
timestampPreciseToDb,
timestampProtocolFromDb,
@@ -104,6 +111,7 @@ import {
getExchangeWireFee,
getScopeForAllExchanges,
} from "./exchanges.js";
+import { EddsaKeypairStrings } from "./index.js";
import {
extractContractData,
generateDepositPermissions,
@@ -217,6 +225,14 @@ export class DepositTransactionContext implements TransactionContext {
depositGroupId: dg.depositGroupId,
trackingState,
deposited,
+ kycPaytoHash: dg.kycInfo?.paytoHash,
+ kycAccessToken: dg.kycInfo?.accessToken,
+ kycUrl: dg.kycInfo
+ ? new URL(
+ `kyc-spa/${dg.kycInfo.accessToken}`,
+ dg.kycInfo.exchangeBaseUrl,
+ ).href
+ : undefined,
...(ort?.lastError ? { error: ort.lastError } : {}),
};
}
@@ -778,6 +794,19 @@ async function processDepositGroupPendingKyc(
throw Error("invalid DB state, in pending(kyc), but no kycInfo present");
}
+ const lastReserveKeypair = await getLastWithdrawalKeyPair(
+ wex,
+ kycInfo.exchangeBaseUrl,
+ );
+ if (!lastReserveKeypair) {
+ // Need to do a KYC transfer
+ throw Error("not supported yet");
+ }
+ const sigResp = await wex.cryptoApi.signWalletKycAuth({
+ accountPriv: lastReserveKeypair.priv,
+ accountPub: lastReserveKeypair.pub,
+ });
+
const url = new URL(
`kyc-check/${kycInfo.paytoHash}`,
kycInfo.exchangeBaseUrl,
@@ -792,6 +821,9 @@ async function processDepositGroupPendingKyc(
return await wex.http.fetch(url.href, {
method: "GET",
cancellationToken: wex.cancellationToken,
+ headers: {
+ ["Account-Owner-Signature"]: sigResp.sig,
+ },
});
},
);
@@ -832,6 +864,42 @@ async function processDepositGroupPendingKyc(
}
/**
+ * Finds the reserve key pair of the most recent withdrawal
+ * with the given exchange.
+ * Returns undefined if no such withdrawal exists.
+ */
+async function getLastWithdrawalKeyPair(
+ wex: WalletExecutionContext,
+ exchangeBaseUrl: string,
+): Promise<EddsaKeypairStrings | undefined> {
+ let candidateTimestamp: AbsoluteTime | undefined = undefined;
+ let candidateRes: EddsaKeypairStrings | undefined = undefined;
+ await wex.db.runAllStoresReadOnlyTx({}, async (tx) => {
+ const withdrawalRecs =
+ await tx.withdrawalGroups.indexes.byExchangeBaseUrl.getAll(
+ exchangeBaseUrl,
+ );
+ for (const rec of withdrawalRecs) {
+ if (!rec.timestampFinish) {
+ continue;
+ }
+ const currTimestamp = timestampAbsoluteFromDb(rec.timestampFinish);
+ if (
+ candidateTimestamp == null ||
+ AbsoluteTime.cmp(currTimestamp, candidateTimestamp) > 0
+ ) {
+ candidateTimestamp = currTimestamp;
+ candidateRes = {
+ priv: rec.reservePriv,
+ pub: rec.reservePub,
+ };
+ }
+ }
+ });
+ return candidateRes;
+}
+
+/**
* Tracking information from the exchange indicated that
* KYC is required. We need to check the KYC info
* and transition the transaction to the KYC required state.
@@ -846,17 +914,33 @@ async function transitionToKycRequired(
const ctx = new DepositTransactionContext(wex, depositGroupId);
+ const lastReserveKeypair = await getLastWithdrawalKeyPair(wex, exchangeUrl);
+ if (!lastReserveKeypair) {
+ // Need to do a KYC transfer
+ throw Error("not supported yet");
+ }
+ const sigResp = await wex.cryptoApi.signWalletKycAuth({
+ accountPriv: lastReserveKeypair.priv,
+ accountPub: lastReserveKeypair.pub,
+ });
+
const url = new URL(`kyc-check/${kycInfo.paytoHash}`, exchangeUrl);
logger.info(`kyc url ${url.href}`);
- const kycStatusReq = await wex.http.fetch(url.href, {
+ const kycStatusResp = await wex.http.fetch(url.href, {
method: "GET",
+ headers: {
+ ["Account-Owner-Signature"]: sigResp.sig,
+ },
});
- if (kycStatusReq.status === HttpStatusCode.Ok) {
+ logger.trace(`response status of initial kyc-check: ${kycStatusResp.status}`);
+ if (kycStatusResp.status === HttpStatusCode.Ok) {
logger.warn("kyc requested, but already fulfilled");
return TaskRunResult.backoff();
- } else if (kycStatusReq.status === HttpStatusCode.Accepted) {
- const kycStatus = await kycStatusReq.json();
- logger.info(`kyc status: ${j2s(kycStatus)}`);
+ } else if (kycStatusResp.status === HttpStatusCode.Accepted) {
+ const statusResp = await readResponseJsonOrThrow(
+ kycStatusResp,
+ codecForAccountKycStatus(),
+ );
const transitionInfo = await wex.db.runReadWriteTx(
{ storeNames: ["depositGroups", "transactionsMeta"] },
async (tx) => {
@@ -868,10 +952,11 @@ async function transitionToKycRequired(
return undefined;
}
const oldTxState = computeDepositTransactionStatus(dg);
+ dg.operationStatus = DepositOperationStatus.PendingKyc;
dg.kycInfo = {
exchangeBaseUrl: exchangeUrl,
paytoHash: kycInfo.paytoHash,
- accessToken: null as any, // FIXME!
+ accessToken: statusResp.access_token,
};
await tx.depositGroups.put(dg);
await ctx.updateTransactionMeta(tx);
@@ -880,9 +965,12 @@ async function transitionToKycRequired(
},
);
notifyTransition(wex, ctx.transactionId, transitionInfo);
- return TaskRunResult.finished();
+ return TaskRunResult.progress();
} else {
- throw Error(`unexpected response from kyc-check (${kycStatusReq.status})`);
+ throwUnexpectedRequestError(
+ kycStatusResp,
+ await readTalerErrorResponse(kycStatusResp),
+ );
}
}
@@ -932,6 +1020,7 @@ async function processDepositGroupPendingTrack(
exchangeBaseUrl,
);
+ logger.trace(`track response: ${j2s(track)}`);
if (track.type === "accepted") {
if (!track.kyc_ok && track.requirement_row !== undefined) {
const paytoHash = encodeCrock(
@@ -940,7 +1029,7 @@ async function processDepositGroupPendingTrack(
const kycInfo: DepositKycInfo = {
paytoHash,
exchangeBaseUrl: exchangeBaseUrl,
- accessToken: null as any, // FIXME!
+ accessToken: undefined,
};
return transitionToKycRequired(
wex,
diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts
index 5be153367..12cfedc9e 100644
--- a/packages/taler-wallet-core/src/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/pay-merchant.ts
@@ -123,7 +123,7 @@ import {
TransactionContext,
TransitionResultType,
} from "./common.js";
-import { EddsaKeypair } from "./crypto/cryptoImplementation.js";
+import { EddsaKeypairStrings } from "./crypto/cryptoImplementation.js";
import {
CoinRecord,
DbCoinSelection,
@@ -1196,7 +1196,7 @@ async function createOrReusePurchase(
return oldProposal.proposalId;
}
- let noncePair: EddsaKeypair;
+ let noncePair: EddsaKeypairStrings;
let shared = false;
if (noncePriv) {
shared = true;
diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts
index d18f8712d..fd6d40bcf 100644
--- a/packages/taler-wallet-core/src/pay-peer-push-credit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-push-credit.ts
@@ -869,12 +869,11 @@ async function processPeerPushCreditKycRequired(
logger.warn("kyc requested, but already fulfilled");
return TaskRunResult.finished();
} else if (kycStatusRes.status === HttpStatusCode.Accepted) {
- const kycStatus = await kycStatusRes.json();
- logger.info(`kyc status: ${j2s(kycStatus)}`);
const statusResp = await readResponseJsonOrThrow(
kycStatusRes,
codecForAccountKycStatus(),
);
+ logger.info(`kyc status: ${j2s(statusResp)}`);
const { transitionInfo, result } = await wex.db.runReadWriteTx(
{ storeNames: ["peerPushCredit", "transactionsMeta"] },
async (tx) => {
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
index 0d47e08c7..1ed600265 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -129,7 +129,7 @@ import {
requireExchangeTosAcceptedOrThrow,
runWithClientCancellation,
} from "./common.js";
-import { EddsaKeypair } from "./crypto/cryptoImplementation.js";
+import { EddsaKeypairStrings } from "./crypto/cryptoImplementation.js";
import {
CoinRecord,
CoinSourceType,
@@ -3060,7 +3060,7 @@ export async function internalPrepareCreateWithdrawalGroup(
exchangeBaseUrl: string | undefined;
forcedWithdrawalGroupId?: string;
forcedDenomSel?: ForcedDenomSel;
- reserveKeyPair?: EddsaKeypair;
+ reserveKeyPair?: EddsaKeypairStrings;
restrictAge?: number;
wgInfo: WgInfo;
},
@@ -3256,7 +3256,7 @@ export async function internalCreateWithdrawalGroup(
amount?: AmountJson;
forcedWithdrawalGroupId?: string;
forcedDenomSel?: ForcedDenomSel;
- reserveKeyPair?: EddsaKeypair;
+ reserveKeyPair?: EddsaKeypairStrings;
restrictAge?: number;
wgInfo: WgInfo;
},
@@ -3846,7 +3846,7 @@ export async function createManualWithdrawal(
);
}
- let reserveKeyPair: EddsaKeypair;
+ let reserveKeyPair: EddsaKeypairStrings;
if (req.forceReservePriv) {
const pubResp = await wex.cryptoApi.eddsaGetPublic({
priv: req.forceReservePriv,