aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/withdraw.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-06-19 16:03:06 +0200
committerFlorian Dold <florian@dold.me>2023-06-19 16:03:06 +0200
commit54f0c82999833132baf83995526025ac56d6fe06 (patch)
treeb0138031c4a0432ec5ecddb62be14b0432112a4b /packages/taler-wallet-core/src/operations/withdraw.ts
parentffa68ce8ddc77bf622af4234696a065cde482554 (diff)
downloadwallet-core-54f0c82999833132baf83995526025ac56d6fe06.tar.xz
wallet-core: fix peer-(push,pull)-debit withdrawal states
Diffstat (limited to 'packages/taler-wallet-core/src/operations/withdraw.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts191
1 files changed, 139 insertions, 52 deletions
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index 26149bd06..88389fd99 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -109,7 +109,11 @@ import {
checkLogicInvariant,
InvariantViolatedError,
} from "../util/invariants.js";
-import { DbAccess, GetReadOnlyAccess } from "../util/query.js";
+import {
+ DbAccess,
+ GetReadOnlyAccess,
+ GetReadWriteAccess,
+} from "../util/query.js";
import {
OperationAttemptResult,
OperationAttemptResultType,
@@ -130,8 +134,13 @@ import {
selectForcedWithdrawalDenominations,
selectWithdrawalDenominations,
} from "../util/coinSelection.js";
-import { PendingTaskType, isWithdrawableDenom } from "../index.js";
import {
+ ExchangeDetailsRecord,
+ PendingTaskType,
+ isWithdrawableDenom,
+} from "../index.js";
+import {
+ TransitionInfo,
constructTransactionIdentifier,
notifyTransition,
stopLongpolling,
@@ -2202,15 +2211,19 @@ async function processReserveBankStatus(
}
}
-/**
- * Create a withdrawal group.
- *
- * If a forcedWithdrawalGroupId is given and a
- * withdrawal group with this ID already exists,
- * the existing one is returned. No conflict checking
- * of the other arguments is done in that case.
- */
-export async function internalCreateWithdrawalGroup(
+export interface PrepareCreateWithdrawalGroupResult {
+ withdrawalGroup: WithdrawalGroupRecord;
+ transactionId: string;
+ creationInfo?: {
+ isTrusted: boolean;
+ isAudited: boolean;
+ amount: AmountJson;
+ canonExchange: string;
+ exchangeDetails: ExchangeDetailsRecord;
+ };
+}
+
+export async function internalPrepareCreateWithdrawalGroup(
ws: InternalWalletState,
args: {
reserveStatus: WithdrawalGroupStatus;
@@ -2222,7 +2235,7 @@ export async function internalCreateWithdrawalGroup(
restrictAge?: number;
wgInfo: WgInfo;
},
-): Promise<WithdrawalGroupRecord> {
+): Promise<PrepareCreateWithdrawalGroupResult> {
const reserveKeyPair =
args.reserveKeyPair ?? (await ws.cryptoApi.createEddsaKeypair({}));
const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now());
@@ -2240,18 +2253,18 @@ export async function internalCreateWithdrawalGroup(
.runReadOnly(async (tx) => {
return tx.withdrawalGroups.get(wgId);
});
+
if (existingWg) {
- return existingWg;
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Withdrawal,
+ withdrawalGroupId: existingWg.withdrawalGroupId,
+ });
+ return { withdrawalGroup: existingWg, transactionId };
}
} else {
withdrawalGroupId = encodeCrock(getRandomBytes(32));
}
- const transactionId = constructTransactionIdentifier({
- tag: TransactionType.Withdrawal,
- withdrawalGroupId,
- });
-
await updateWithdrawalDenoms(ws, canonExchange);
const denoms = await getCandidateWithdrawalDenoms(ws, canonExchange);
@@ -2302,8 +2315,112 @@ export async function internalCreateWithdrawalGroup(
ws,
exchangeInfo.exchange,
);
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Withdrawal,
+ withdrawalGroupId: withdrawalGroup.withdrawalGroupId,
+ });
- const transitionInfo = await ws.db
+ return {
+ withdrawalGroup,
+ transactionId,
+ creationInfo: {
+ isAudited,
+ isTrusted,
+ canonExchange,
+ amount,
+ exchangeDetails,
+ },
+ };
+}
+
+export interface PerformCreateWithdrawalGroupResult {
+ withdrawalGroup: WithdrawalGroupRecord;
+ transitionInfo: TransitionInfo | undefined;
+}
+
+export async function internalPerformCreateWithdrawalGroup(
+ ws: InternalWalletState,
+ tx: GetReadWriteAccess<{
+ withdrawalGroups: typeof WalletStoresV1.withdrawalGroups;
+ reserves: typeof WalletStoresV1.reserves;
+ exchanges: typeof WalletStoresV1.exchanges;
+ exchangeTrust: typeof WalletStoresV1.exchangeTrust;
+ }>,
+ prep: PrepareCreateWithdrawalGroupResult,
+): Promise<PerformCreateWithdrawalGroupResult> {
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Withdrawal,
+ withdrawalGroupId: prep.withdrawalGroup.withdrawalGroupId,
+ });
+ const { withdrawalGroup } = prep;
+ if (!prep.creationInfo) {
+ return { withdrawalGroup, transitionInfo: undefined };
+ }
+ const { isAudited, isTrusted, amount, canonExchange, exchangeDetails } =
+ prep.creationInfo;
+
+ await tx.withdrawalGroups.add(withdrawalGroup);
+ await tx.reserves.put({
+ reservePub: withdrawalGroup.reservePub,
+ reservePriv: withdrawalGroup.reservePriv,
+ });
+
+ const exchange = await tx.exchanges.get(withdrawalGroup.exchangeBaseUrl);
+ if (exchange) {
+ exchange.lastWithdrawal = TalerPreciseTimestamp.now();
+ await tx.exchanges.put(exchange);
+ }
+
+ if (!isAudited && !isTrusted) {
+ await tx.exchangeTrust.put({
+ currency: amount.currency,
+ exchangeBaseUrl: canonExchange,
+ exchangeMasterPub: exchangeDetails.masterPublicKey,
+ uids: [encodeCrock(getRandomBytes(32))],
+ });
+ }
+
+ const oldTxState = {
+ major: TransactionMajorState.None,
+ minor: undefined,
+ };
+ const newTxState = computeWithdrawalTransactionStatus(withdrawalGroup);
+ const transitionInfo = {
+ oldTxState,
+ newTxState,
+ };
+ notifyTransition(ws, transactionId, transitionInfo);
+
+ return { withdrawalGroup, transitionInfo };
+}
+
+/**
+ * Create a withdrawal group.
+ *
+ * If a forcedWithdrawalGroupId is given and a
+ * withdrawal group with this ID already exists,
+ * the existing one is returned. No conflict checking
+ * of the other arguments is done in that case.
+ */
+export async function internalCreateWithdrawalGroup(
+ ws: InternalWalletState,
+ args: {
+ reserveStatus: WithdrawalGroupStatus;
+ amount: AmountJson;
+ exchangeBaseUrl: string;
+ forcedWithdrawalGroupId?: string;
+ forcedDenomSel?: ForcedDenomSel;
+ reserveKeyPair?: EddsaKeypair;
+ restrictAge?: number;
+ wgInfo: WgInfo;
+ },
+): Promise<WithdrawalGroupRecord> {
+ const prep = await internalPrepareCreateWithdrawalGroup(ws, args);
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Withdrawal,
+ withdrawalGroupId: prep.withdrawalGroup.withdrawalGroupId,
+ });
+ const res = await ws.db
.mktx((x) => [
x.withdrawalGroups,
x.reserves,
@@ -2312,40 +2429,10 @@ export async function internalCreateWithdrawalGroup(
x.exchangeTrust,
])
.runReadWrite(async (tx) => {
- await tx.withdrawalGroups.add(withdrawalGroup);
- await tx.reserves.put({
- reservePub: withdrawalGroup.reservePub,
- reservePriv: withdrawalGroup.reservePriv,
- });
-
- const exchange = await tx.exchanges.get(withdrawalGroup.exchangeBaseUrl);
- if (exchange) {
- exchange.lastWithdrawal = TalerPreciseTimestamp.now();
- await tx.exchanges.put(exchange);
- }
-
- if (!isAudited && !isTrusted) {
- await tx.exchangeTrust.put({
- currency: amount.currency,
- exchangeBaseUrl: canonExchange,
- exchangeMasterPub: exchangeDetails.masterPublicKey,
- uids: [encodeCrock(getRandomBytes(32))],
- });
- }
-
- const oldTxState = {
- major: TransactionMajorState.None,
- };
- const newTxState = computeWithdrawalTransactionStatus(withdrawalGroup);
- return {
- oldTxState,
- newTxState,
- };
+ return await internalPerformCreateWithdrawalGroup(ws, tx, prep);
});
-
- notifyTransition(ws, transactionId, transitionInfo);
-
- return withdrawalGroup;
+ notifyTransition(ws, transactionId, res.transitionInfo);
+ return res.withdrawalGroup;
}
export async function acceptWithdrawalFromUri(