nominal typing for taskId, also fixing transactionId reference

This commit is contained in:
Sebastian 2023-05-22 13:13:40 -03:00
parent 76d2524b8b
commit 708cf016e4
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
10 changed files with 195 additions and 148 deletions

View File

@ -54,6 +54,7 @@ import {
RetryInfo, RetryInfo,
} from "../util/retries.js"; } from "../util/retries.js";
import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js"; import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js";
import { TaskId } from "../pending-types.js";
const logger = new Logger("operations/common.ts"); const logger = new Logger("operations/common.ts");
@ -260,7 +261,7 @@ export async function storeOperationPending(
export async function runOperationWithErrorReporting<T1, T2>( export async function runOperationWithErrorReporting<T1, T2>(
ws: InternalWalletState, ws: InternalWalletState,
opId: string, opId: TaskId,
f: () => Promise<OperationAttemptResult<T1, T2>>, f: () => Promise<OperationAttemptResult<T1, T2>>,
): Promise<OperationAttemptResult<T1, T2>> { ): Promise<OperationAttemptResult<T1, T2>> {
let maybeError: TalerErrorDetail | undefined; let maybeError: TalerErrorDetail | undefined;
@ -369,7 +370,7 @@ export enum TombstoneTag {
export function makeTransactionId( export function makeTransactionId(
type: TransactionType, type: TransactionType,
...args: string[] ...args: string[]
): TransactionIdStr { ): string {
return `txn:${type}:${args.map((x) => encodeURIComponent(x)).join(":")}`; return `txn:${type}:${args.map((x) => encodeURIComponent(x)).join(":")}`;
} }
@ -401,10 +402,7 @@ export function parseId(
/** /**
* Create an event ID from the type and the primary key for the event. * Create an event ID from the type and the primary key for the event.
*/ */
export function makeTombstoneId( export function makeTombstoneId(type: TombstoneTag, ...args: string[]): string {
type: TombstoneTag,
...args: string[]
): TombstoneIdStr {
return `tmb:${type}:${args.map((x) => encodeURIComponent(x)).join(":")}`; return `tmb:${type}:${args.map((x) => encodeURIComponent(x)).join(":")}`;
} }

View File

@ -844,7 +844,11 @@ async function handleInsufficientFunds(
payInfo.payCoinSelectionUid = encodeCrock(getRandomBytes(32)); payInfo.payCoinSelectionUid = encodeCrock(getRandomBytes(32));
await tx.purchases.put(p); await tx.purchases.put(p);
await spendCoins(ws, tx, { await spendCoins(ws, tx, {
allocationId: `txn:proposal:${p.proposalId}`, // allocationId: `txn:proposal:${p.proposalId}`,
allocationId: constructTransactionIdentifier({
tag: TransactionType.Payment,
proposalId: proposalId,
}),
coinPubs: payInfo.payCoinSelection.coinPubs, coinPubs: payInfo.payCoinSelection.coinPubs,
contributions: payInfo.payCoinSelection.coinContributions.map((x) => contributions: payInfo.payCoinSelection.coinContributions.map((x) =>
Amounts.parseOrThrow(x), Amounts.parseOrThrow(x),
@ -1199,7 +1203,10 @@ export async function runPayForConfirmPay(
return { return {
type: ConfirmPayResultType.Done, type: ConfirmPayResultType.Done,
contractTerms: d.contractTermsRaw, contractTerms: d.contractTermsRaw,
transactionId: makeTransactionId(TransactionType.Payment, proposalId), transactionId: constructTransactionIdentifier({
tag: TransactionType.Payment,
proposalId,
}),
}; };
} }
case OperationAttemptResultType.Error: { case OperationAttemptResultType.Error: {
@ -1210,14 +1217,20 @@ export async function runPayForConfirmPay(
return { return {
type: ConfirmPayResultType.Pending, type: ConfirmPayResultType.Pending,
lastError: opRetry?.lastError, lastError: opRetry?.lastError,
transactionId: makeTransactionId(TransactionType.Payment, proposalId), transactionId: constructTransactionIdentifier({
tag: TransactionType.Payment,
proposalId,
}),
}; };
} }
case OperationAttemptResultType.Pending: case OperationAttemptResultType.Pending:
logger.trace("reporting pending as confirmPay response"); logger.trace("reporting pending as confirmPay response");
return { return {
type: ConfirmPayResultType.Pending, type: ConfirmPayResultType.Pending,
transactionId: makeTransactionId(TransactionType.Payment, proposalId), transactionId: constructTransactionIdentifier({
tag: TransactionType.Payment,
proposalId,
}),
lastError: undefined, lastError: undefined,
}; };
case OperationAttemptResultType.Longpoll: case OperationAttemptResultType.Longpoll:
@ -1344,7 +1357,11 @@ export async function confirmPay(
p.purchaseStatus = PurchaseStatus.Paying; p.purchaseStatus = PurchaseStatus.Paying;
await tx.purchases.put(p); await tx.purchases.put(p);
await spendCoins(ws, tx, { await spendCoins(ws, tx, {
allocationId: `txn:proposal:${p.proposalId}`, //`txn:proposal:${p.proposalId}`
allocationId: constructTransactionIdentifier({
tag: TransactionType.Payment,
proposalId: proposalId,
}),
coinPubs: coinSelection.coinPubs, coinPubs: coinSelection.coinPubs,
contributions: coinSelection.coinContributions.map((x) => contributions: coinSelection.coinContributions.map((x) =>
Amounts.parseOrThrow(x), Amounts.parseOrThrow(x),
@ -2072,7 +2089,7 @@ export async function startRefundQueryForUri(
await startQueryRefund(ws, proposalId); await startQueryRefund(ws, proposalId);
return { return {
transactionId, transactionId,
} };
} }
export async function startQueryRefund( export async function startQueryRefund(
@ -2357,6 +2374,6 @@ export function computeRefundTransactionState(
case RefundGroupStatus.Pending: case RefundGroupStatus.Pending:
return { return {
major: TransactionMajorState.Pending, major: TransactionMajorState.Pending,
} };
} }
} }

View File

@ -705,7 +705,11 @@ export async function initiatePeerPushDebit(
// we might want to mark the coins as used and spend them // we might want to mark the coins as used and spend them
// after we've been able to create the purse. // after we've been able to create the purse.
await spendCoins(ws, tx, { await spendCoins(ws, tx, {
allocationId: `txn:peer-push-debit:${pursePair.pub}`, // allocationId: `txn:peer-push-debit:${pursePair.pub}`,
allocationId: constructTransactionIdentifier({
tag: TransactionType.PeerPushDebit,
pursePub: pursePair.pub,
}),
coinPubs: sel.coins.map((x) => x.coinPub), coinPubs: sel.coins.map((x) => x.coinPub),
contributions: sel.coins.map((x) => contributions: sel.coins.map((x) =>
Amounts.parseOrThrow(x.contribution), Amounts.parseOrThrow(x.contribution),
@ -1280,7 +1284,11 @@ export async function confirmPeerPullDebit(
]) ])
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
await spendCoins(ws, tx, { await spendCoins(ws, tx, {
allocationId: `txn:peer-pull-debit:${req.peerPullPaymentIncomingId}`, // allocationId: `txn:peer-pull-debit:${req.peerPullPaymentIncomingId}`,
allocationId: constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
peerPullPaymentIncomingId: req.peerPullPaymentIncomingId,
}),
coinPubs: sel.coins.map((x) => x.coinPub), coinPubs: sel.coins.map((x) => x.coinPub),
contributions: sel.coins.map((x) => contributions: sel.coins.map((x) =>
Amounts.parseOrThrow(x.contribution), Amounts.parseOrThrow(x.contribution),

View File

@ -36,6 +36,7 @@ import {
import { import {
PendingOperationsResponse, PendingOperationsResponse,
PendingTaskType, PendingTaskType,
TaskId,
} from "../pending-types.js"; } from "../pending-types.js";
import { AbsoluteTime } from "@gnu-taler/taler-util"; import { AbsoluteTime } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
@ -45,10 +46,10 @@ import { GlobalIDB } from "@gnu-taler/idb-bridge";
function getPendingCommon( function getPendingCommon(
ws: InternalWalletState, ws: InternalWalletState,
opTag: string, opTag: TaskId,
timestampDue: AbsoluteTime, timestampDue: AbsoluteTime,
): { ): {
id: string; id: TaskId;
isDue: boolean; isDue: boolean;
timestampDue: AbsoluteTime; timestampDue: AbsoluteTime;
isLongpolling: boolean; isLongpolling: boolean;

View File

@ -935,7 +935,7 @@ export async function calculateRefreshOutput(
return { return {
outputPerCoin: estimatedOutputPerCoin, outputPerCoin: estimatedOutputPerCoin,
} };
} }
async function applyRefresh( async function applyRefresh(
@ -990,7 +990,11 @@ async function applyRefresh(
if (!coin.spendAllocation) { if (!coin.spendAllocation) {
coin.spendAllocation = { coin.spendAllocation = {
amount: Amounts.stringify(ocp.amount), amount: Amounts.stringify(ocp.amount),
id: `txn:refresh:${refreshGroupId}`, // id: `txn:refresh:${refreshGroupId}`,
id: constructTransactionIdentifier({
tag: TransactionType.Refresh,
refreshGroupId,
}),
}; };
} }
await tx.coins.put(coin); await tx.coins.put(coin);

View File

@ -67,6 +67,7 @@ import {
updateWithdrawalDenoms, updateWithdrawalDenoms,
} from "./withdraw.js"; } from "./withdraw.js";
import { selectWithdrawalDenominations } from "../util/coinSelection.js"; import { selectWithdrawalDenominations } from "../util/coinSelection.js";
import { constructTransactionIdentifier } from "./transactions.js";
const logger = new Logger("operations/tip.ts"); const logger = new Logger("operations/tip.ts");
@ -395,7 +396,10 @@ export async function acceptTip(
//FIXME: if tip is not found the behavior of the function is the same //FIXME: if tip is not found the behavior of the function is the same
// as the tip was found and finished // as the tip was found and finished
return { return {
transactionId: makeTransactionId(TransactionType.Tip, tipId), transactionId: constructTransactionIdentifier({
tag: TransactionType.Tip,
walletTipId: tipId,
}),
next_url: found?.next_url, next_url: found?.next_url,
}; };
} }

View File

@ -31,6 +31,8 @@ import {
PeerContractTerms, PeerContractTerms,
RefundInfoShort, RefundInfoShort,
RefundPaymentInfo, RefundPaymentInfo,
stringifyPayPullUri,
stringifyPayPushUri,
TalerErrorCode, TalerErrorCode,
TalerProtocolTimestamp, TalerProtocolTimestamp,
Transaction, Transaction,
@ -94,8 +96,17 @@ import {
extractContractData, extractContractData,
processPurchasePay, processPurchasePay,
} from "./pay-merchant.js"; } from "./pay-merchant.js";
import { computePeerPullCreditTransactionState, computePeerPullDebitTransactionState, computePeerPushCreditTransactionState, computePeerPushDebitTransactionState, processPeerPullCredit } from "./pay-peer.js"; import {
import { computeRefreshTransactionState, processRefreshGroup } from "./refresh.js"; computePeerPullCreditTransactionState,
computePeerPullDebitTransactionState,
computePeerPushCreditTransactionState,
computePeerPushDebitTransactionState,
processPeerPullCredit,
} from "./pay-peer.js";
import {
computeRefreshTransactionState,
processRefreshGroup,
} from "./refresh.js";
import { computeTipTransactionStatus, processTip } from "./tip.js"; import { computeTipTransactionStatus, processTip } from "./tip.js";
import { import {
abortWithdrawalTransaction, abortWithdrawalTransaction,
@ -378,14 +389,14 @@ function buildTransactionForPushPaymentDebit(
: ExtendedStatus.Done, : ExtendedStatus.Done,
pending: pi.status != PeerPushPaymentInitiationStatus.Done, pending: pi.status != PeerPushPaymentInitiationStatus.Done,
timestamp: pi.timestampCreated, timestamp: pi.timestampCreated,
talerUri: constructPayPushUri({ talerUri: stringifyPayPushUri({
exchangeBaseUrl: pi.exchangeBaseUrl, exchangeBaseUrl: pi.exchangeBaseUrl,
contractPriv: pi.contractPriv, contractPriv: pi.contractPriv,
}), }),
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.PeerPushDebit, tag: TransactionType.PeerPushDebit,
pi.pursePub, pursePub: pi.pursePub,
), }),
...(ort?.lastError ? { error: ort.lastError } : {}), ...(ort?.lastError ? { error: ort.lastError } : {}),
}; };
} }
@ -410,10 +421,10 @@ function buildTransactionForPullPaymentDebit(
summary: pi.contractTerms.summary, summary: pi.contractTerms.summary,
}, },
timestamp: pi.timestampCreated, timestamp: pi.timestampCreated,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.PeerPullDebit, tag: TransactionType.PeerPullDebit,
pi.peerPullPaymentIncomingId, peerPullPaymentIncomingId: pi.peerPullPaymentIncomingId,
), }),
...(ort?.lastError ? { error: ort.lastError } : {}), ...(ort?.lastError ? { error: ort.lastError } : {}),
}; };
} }
@ -460,14 +471,14 @@ function buildTransactionForPeerPullCredit(
expiration: wsr.wgInfo.contractTerms.purse_expiration, expiration: wsr.wgInfo.contractTerms.purse_expiration,
summary: wsr.wgInfo.contractTerms.summary, summary: wsr.wgInfo.contractTerms.summary,
}, },
talerUri: constructPayPullUri({ talerUri: stringifyPayPullUri({
exchangeBaseUrl: wsr.exchangeBaseUrl, exchangeBaseUrl: wsr.exchangeBaseUrl,
contractPriv: wsr.wgInfo.contractPriv, contractPriv: wsr.wgInfo.contractPriv,
}), }),
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.PeerPullCredit, tag: TransactionType.PeerPullCredit,
pullCredit.pursePub, pursePub: pullCredit.pursePub,
), }),
frozen: false, frozen: false,
...(wsrOrt?.lastError ...(wsrOrt?.lastError
? { ? {
@ -493,14 +504,14 @@ function buildTransactionForPeerPullCredit(
expiration: peerContractTerms.purse_expiration, expiration: peerContractTerms.purse_expiration,
summary: peerContractTerms.summary, summary: peerContractTerms.summary,
}, },
talerUri: constructPayPullUri({ talerUri: stringifyPayPullUri({
exchangeBaseUrl: pullCredit.exchangeBaseUrl, exchangeBaseUrl: pullCredit.exchangeBaseUrl,
contractPriv: pullCredit.contractPriv, contractPriv: pullCredit.contractPriv,
}), }),
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.PeerPullCredit, tag: TransactionType.PeerPullCredit,
pullCredit.pursePub, pursePub: pullCredit.pursePub,
), }),
frozen: false, frozen: false,
...(pullCreditOrt?.lastError ? { error: pullCreditOrt.lastError } : {}), ...(pullCreditOrt?.lastError ? { error: pullCreditOrt.lastError } : {}),
}; };
@ -533,10 +544,10 @@ function buildTransactionForPeerPushCredit(
: ExtendedStatus.Pending, : ExtendedStatus.Pending,
pending: !wsr.timestampFinish, pending: !wsr.timestampFinish,
timestamp: wsr.timestampStart, timestamp: wsr.timestampStart,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.PeerPushCredit, tag: TransactionType.PeerPushCredit,
pushInc.peerPushPaymentIncomingId, peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId,
), }),
frozen: false, frozen: false,
...(wsrOrt?.lastError ? { error: wsrOrt.lastError } : {}), ...(wsrOrt?.lastError ? { error: wsrOrt.lastError } : {}),
}; };
@ -556,10 +567,10 @@ function buildTransactionForPeerPushCredit(
extendedStatus: ExtendedStatus.Pending, extendedStatus: ExtendedStatus.Pending,
pending: true, pending: true,
timestamp: pushInc.timestamp, timestamp: pushInc.timestamp,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.PeerPushCredit, tag: TransactionType.PeerPushCredit,
pushInc.peerPushPaymentIncomingId, peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId,
), }),
frozen: false, frozen: false,
...(pushOrt?.lastError ? { error: pushOrt.lastError } : {}), ...(pushOrt?.lastError ? { error: pushOrt.lastError } : {}),
}; };
@ -592,10 +603,10 @@ function buildTransactionForBankIntegratedWithdraw(
: ExtendedStatus.Pending, : ExtendedStatus.Pending,
pending: !wgRecord.timestampFinish, pending: !wgRecord.timestampFinish,
timestamp: wgRecord.timestampStart, timestamp: wgRecord.timestampStart,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.Withdrawal, tag: TransactionType.Withdrawal,
wgRecord.withdrawalGroupId, withdrawalGroupId: wgRecord.withdrawalGroupId,
), }),
frozen: false, frozen: false,
...(ort?.lastError ? { error: ort.lastError } : {}), ...(ort?.lastError ? { error: ort.lastError } : {}),
}; };
@ -639,10 +650,10 @@ function buildTransactionForManualWithdraw(
: ExtendedStatus.Pending, : ExtendedStatus.Pending,
pending: !withdrawalGroup.timestampFinish, pending: !withdrawalGroup.timestampFinish,
timestamp: withdrawalGroup.timestampStart, timestamp: withdrawalGroup.timestampStart,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.Withdrawal, tag: TransactionType.Withdrawal,
withdrawalGroup.withdrawalGroupId, withdrawalGroupId: withdrawalGroup.withdrawalGroupId,
), }),
frozen: false, frozen: false,
...(ort?.lastError ? { error: ort.lastError } : {}), ...(ort?.lastError ? { error: ort.lastError } : {}),
}; };
@ -668,7 +679,7 @@ function buildTransactionForRefund(
amountRaw: refundRecord.amountRaw, amountRaw: refundRecord.amountRaw,
refundedTransactionId: constructTransactionIdentifier({ refundedTransactionId: constructTransactionIdentifier({
tag: TransactionType.Payment, tag: TransactionType.Payment,
proposalId: refundRecord.proposalId proposalId: refundRecord.proposalId,
}), }),
timestamp: refundRecord.timestampCreated, timestamp: refundRecord.timestampCreated,
transactionId: constructTransactionIdentifier({ transactionId: constructTransactionIdentifier({
@ -680,7 +691,7 @@ function buildTransactionForRefund(
frozen: false, frozen: false,
pending: false, pending: false,
paymentInfo, paymentInfo,
} };
} }
function buildTransactionForRefresh( function buildTransactionForRefresh(
@ -726,10 +737,10 @@ function buildTransactionForRefresh(
: ExtendedStatus.Pending, : ExtendedStatus.Pending,
pending: extendedStatus == ExtendedStatus.Pending, pending: extendedStatus == ExtendedStatus.Pending,
timestamp: refreshGroupRecord.timestampCreated, timestamp: refreshGroupRecord.timestampCreated,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.Refresh, tag: TransactionType.Refresh,
refreshGroupRecord.refreshGroupId, refreshGroupId: refreshGroupRecord.refreshGroupId,
), }),
frozen: false, frozen: false,
...(ort?.lastError ? { error: ort.lastError } : {}), ...(ort?.lastError ? { error: ort.lastError } : {}),
}; };
@ -759,10 +770,10 @@ function buildTransactionForDeposit(
timestamp: dg.timestampCreated, timestamp: dg.timestampCreated,
targetPaytoUri: dg.wire.payto_uri, targetPaytoUri: dg.wire.payto_uri,
wireTransferDeadline: dg.contractTermsRaw.wire_transfer_deadline, wireTransferDeadline: dg.contractTermsRaw.wire_transfer_deadline,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.Deposit, tag: TransactionType.Deposit,
dg.depositGroupId, depositGroupId: dg.depositGroupId,
), }),
wireTransferProgress: wireTransferProgress:
(100 * (100 *
dg.transactionPerCoin.reduce( dg.transactionPerCoin.reduce(
@ -794,16 +805,15 @@ function buildTransactionForTip(
pending: !tipRecord.pickedUpTimestamp, pending: !tipRecord.pickedUpTimestamp,
frozen: false, frozen: false,
timestamp: tipRecord.acceptedTimestamp, timestamp: tipRecord.acceptedTimestamp,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.Tip, tag: TransactionType.Tip,
tipRecord.walletTipId, walletTipId: tipRecord.walletTipId,
), }),
merchantBaseUrl: tipRecord.merchantBaseUrl, merchantBaseUrl: tipRecord.merchantBaseUrl,
...(ort?.lastError ? { error: ort.lastError } : {}), ...(ort?.lastError ? { error: ort.lastError } : {}),
}; };
} }
async function buildTransactionForPurchase( async function buildTransactionForPurchase(
purchaseRecord: PurchaseRecord, purchaseRecord: PurchaseRecord,
contractData: WalletContractData, contractData: WalletContractData,
@ -876,17 +886,17 @@ async function buildTransactionForPurchase(
refunds, refunds,
posConfirmation: purchaseRecord.posConfirmation, posConfirmation: purchaseRecord.posConfirmation,
timestamp, timestamp,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.Payment, tag: TransactionType.Payment,
purchaseRecord.proposalId, proposalId: purchaseRecord.proposalId,
), }),
proposalId: purchaseRecord.proposalId, proposalId: purchaseRecord.proposalId,
info, info,
refundQueryActive: refundQueryActive:
purchaseRecord.purchaseStatus === PurchaseStatus.QueryingRefund, purchaseRecord.purchaseStatus === PurchaseStatus.QueryingRefund,
frozen: frozen:
purchaseRecord.purchaseStatus === PurchaseStatus.AbortedIncompletePayment ?? purchaseRecord.purchaseStatus ===
false, PurchaseStatus.AbortedIncompletePayment ?? false,
...(ort?.lastError ? { error: ort.lastError } : {}), ...(ort?.lastError ? { error: ort.lastError } : {}),
}; };
} }
@ -1253,25 +1263,25 @@ export function constructTransactionIdentifier(
): TransactionIdStr { ): TransactionIdStr {
switch (pTxId.tag) { switch (pTxId.tag) {
case TransactionType.Deposit: case TransactionType.Deposit:
return `txn:${pTxId.tag}:${pTxId.depositGroupId}`; return `txn:${pTxId.tag}:${pTxId.depositGroupId}` as TransactionIdStr;
case TransactionType.Payment: case TransactionType.Payment:
return `txn:${pTxId.tag}:${pTxId.proposalId}`; return `txn:${pTxId.tag}:${pTxId.proposalId}` as TransactionIdStr;
case TransactionType.PeerPullCredit: case TransactionType.PeerPullCredit:
return `txn:${pTxId.tag}:${pTxId.pursePub}`; return `txn:${pTxId.tag}:${pTxId.pursePub}` as TransactionIdStr;
case TransactionType.PeerPullDebit: case TransactionType.PeerPullDebit:
return `txn:${pTxId.tag}:${pTxId.peerPullPaymentIncomingId}`; return `txn:${pTxId.tag}:${pTxId.peerPullPaymentIncomingId}` as TransactionIdStr;
case TransactionType.PeerPushCredit: case TransactionType.PeerPushCredit:
return `txn:${pTxId.tag}:${pTxId.peerPushPaymentIncomingId}`; return `txn:${pTxId.tag}:${pTxId.peerPushPaymentIncomingId}` as TransactionIdStr;
case TransactionType.PeerPushDebit: case TransactionType.PeerPushDebit:
return `txn:${pTxId.tag}:${pTxId.pursePub}`; return `txn:${pTxId.tag}:${pTxId.pursePub}` as TransactionIdStr;
case TransactionType.Refresh: case TransactionType.Refresh:
return `txn:${pTxId.tag}:${pTxId.refreshGroupId}`; return `txn:${pTxId.tag}:${pTxId.refreshGroupId}` as TransactionIdStr;
case TransactionType.Refund: case TransactionType.Refund:
return `txn:${pTxId.tag}:${pTxId.refundGroupId}`; return `txn:${pTxId.tag}:${pTxId.refundGroupId}` as TransactionIdStr;
case TransactionType.Tip: case TransactionType.Tip:
return `txn:${pTxId.tag}:${pTxId.walletTipId}`; return `txn:${pTxId.tag}:${pTxId.walletTipId}` as TransactionIdStr;
case TransactionType.Withdrawal: case TransactionType.Withdrawal:
return `txn:${pTxId.tag}:${pTxId.withdrawalGroupId}`; return `txn:${pTxId.tag}:${pTxId.withdrawalGroupId}` as TransactionIdStr;
default: default:
assertUnreachable(pTxId); assertUnreachable(pTxId);
} }

View File

@ -482,7 +482,7 @@ export function computeWithdrawalTransactionStatus(
return { return {
major: TransactionMajorState.Aborted, major: TransactionMajorState.Aborted,
minor: TransactionMinorState.Exchange, minor: TransactionMinorState.Exchange,
} };
} }
} }
@ -1997,7 +1997,7 @@ async function processReserveBankStatus(
return { return {
oldTxState, oldTxState,
newTxState, newTxState,
} };
}); });
notifyTransition(ws, transactionId, transitionInfo); notifyTransition(ws, transactionId, transitionInfo);
return { return {
@ -2059,7 +2059,7 @@ async function processReserveBankStatus(
return { return {
oldTxState, oldTxState,
newTxState, newTxState,
} };
}); });
notifyTransition(ws, transactionId, transitionInfo); notifyTransition(ws, transactionId, transitionInfo);
@ -2208,12 +2208,12 @@ export async function internalCreateWithdrawalGroup(
const oldTxState = { const oldTxState = {
major: TransactionMajorState.None, major: TransactionMajorState.None,
} };
const newTxState = computeWithdrawalTransactionStatus(withdrawalGroup); const newTxState = computeWithdrawalTransactionStatus(withdrawalGroup);
return { return {
oldTxState, oldTxState,
newTxState, newTxState,
} };
}); });
notifyTransition(ws, transactionId, transitionInfo); notifyTransition(ws, transactionId, transitionInfo);
@ -2253,10 +2253,10 @@ export async function acceptWithdrawalFromUri(
return { return {
reservePub: existingWithdrawalGroup.reservePub, reservePub: existingWithdrawalGroup.reservePub,
confirmTransferUrl: url, confirmTransferUrl: url,
transactionId: makeTransactionId( transactionId: constructTransactionIdentifier({
TransactionType.Withdrawal, tag: TransactionType.Withdrawal,
existingWithdrawalGroup.withdrawalGroupId, withdrawalGroupId: existingWithdrawalGroup.withdrawalGroupId,
), }),
}; };
} }
@ -2290,8 +2290,8 @@ export async function acceptWithdrawalFromUri(
}); });
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
const transactionId = constructTaskIdentifier({ const transactionId = constructTransactionIdentifier({
tag: PendingTaskType.Withdraw, tag: TransactionType.Withdrawal,
withdrawalGroupId, withdrawalGroupId,
}); });
@ -2301,7 +2301,9 @@ export async function acceptWithdrawalFromUri(
const processedWithdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, { const processedWithdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, {
withdrawalGroupId, withdrawalGroupId,
}); });
if (processedWithdrawalGroup?.status === WithdrawalGroupStatus.FailedBankAborted) { if (
processedWithdrawalGroup?.status === WithdrawalGroupStatus.FailedBankAborted
) {
throw TalerError.fromDetail( throw TalerError.fromDetail(
TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK, TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK,
{}, {},
@ -2346,8 +2348,8 @@ export async function createManualWithdrawal(
}); });
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
const transactionId = constructTaskIdentifier({ const transactionId = constructTransactionIdentifier({
tag: PendingTaskType.Withdraw, tag: TransactionType.Withdrawal,
withdrawalGroupId, withdrawalGroupId,
}); });

View File

@ -191,6 +191,9 @@ export interface PendingDepositTask {
depositGroupId: string; depositGroupId: string;
} }
declare const __taskId: unique symbol;
export type TaskId = string & { [__taskId]: true };
/** /**
* Fields that are present in every pending operation. * Fields that are present in every pending operation.
*/ */
@ -203,7 +206,7 @@ export interface PendingTaskInfoCommon {
/** /**
* Unique identifier for the pending task. * Unique identifier for the pending task.
*/ */
id: string; id: TaskId;
/** /**
* Set to true if the operation indicates that something is really in progress, * Set to true if the operation indicates that something is really in progress,

View File

@ -44,7 +44,7 @@ import {
} from "../db.js"; } from "../db.js";
import { TalerError } from "@gnu-taler/taler-util"; import { TalerError } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import { PendingTaskType } from "../pending-types.js"; import { PendingTaskType, TaskId } from "../pending-types.js";
import { GetReadWriteAccess } from "./query.js"; import { GetReadWriteAccess } from "./query.js";
import { assertUnreachable } from "./assertUnreachable.js"; import { assertUnreachable } from "./assertUnreachable.js";
@ -79,7 +79,7 @@ export namespace OperationAttemptResult {
export function longpoll(): OperationAttemptResult<unknown, unknown> { export function longpoll(): OperationAttemptResult<unknown, unknown> {
return { return {
type: OperationAttemptResultType.Longpoll, type: OperationAttemptResultType.Longpoll,
} };
} }
} }
@ -214,89 +214,89 @@ export function parseTaskIdentifier(x: string): ParsedTaskIdentifier {
throw Error("not yet implemented"); throw Error("not yet implemented");
} }
export function constructTaskIdentifier(p: ParsedTaskIdentifier): string { export function constructTaskIdentifier(p: ParsedTaskIdentifier): TaskId {
switch (p.tag) { switch (p.tag) {
case PendingTaskType.Backup: case PendingTaskType.Backup:
return `${p.tag}:${p.backupProviderBaseUrl}`; return `${p.tag}:${p.backupProviderBaseUrl}` as TaskId;
case PendingTaskType.Deposit: case PendingTaskType.Deposit:
return `${p.tag}:${p.depositGroupId}`; return `${p.tag}:${p.depositGroupId}` as TaskId;
case PendingTaskType.ExchangeCheckRefresh: case PendingTaskType.ExchangeCheckRefresh:
return `${p.tag}:${p.exchangeBaseUrl}`; return `${p.tag}:${p.exchangeBaseUrl}` as TaskId;
case PendingTaskType.ExchangeUpdate: case PendingTaskType.ExchangeUpdate:
return `${p.tag}:${p.exchangeBaseUrl}`; return `${p.tag}:${p.exchangeBaseUrl}` as TaskId;
case PendingTaskType.PeerPullDebit: case PendingTaskType.PeerPullDebit:
return `${p.tag}:${p.peerPullPaymentIncomingId}`; return `${p.tag}:${p.peerPullPaymentIncomingId}` as TaskId;
case PendingTaskType.PeerPushCredit: case PendingTaskType.PeerPushCredit:
return `${p.tag}:${p.peerPushPaymentIncomingId}`; return `${p.tag}:${p.peerPushPaymentIncomingId}` as TaskId;
case PendingTaskType.PeerPullCredit: case PendingTaskType.PeerPullCredit:
return `${p.tag}:${p.pursePub}`; return `${p.tag}:${p.pursePub}` as TaskId;
case PendingTaskType.PeerPushDebit: case PendingTaskType.PeerPushDebit:
return `${p.tag}:${p.pursePub}`; return `${p.tag}:${p.pursePub}` as TaskId;
case PendingTaskType.Purchase: case PendingTaskType.Purchase:
return `${p.tag}:${p.proposalId}`; return `${p.tag}:${p.proposalId}` as TaskId;
case PendingTaskType.Recoup: case PendingTaskType.Recoup:
return `${p.tag}:${p.recoupGroupId}`; return `${p.tag}:${p.recoupGroupId}` as TaskId;
case PendingTaskType.Refresh: case PendingTaskType.Refresh:
return `${p.tag}:${p.refreshGroupId}`; return `${p.tag}:${p.refreshGroupId}` as TaskId;
case PendingTaskType.TipPickup: case PendingTaskType.TipPickup:
return `${p.tag}:${p.walletTipId}`; return `${p.tag}:${p.walletTipId}` as TaskId;
case PendingTaskType.Withdraw: case PendingTaskType.Withdraw:
return `${p.tag}:${p.withdrawalGroupId}`; return `${p.tag}:${p.withdrawalGroupId}` as TaskId;
default: default:
assertUnreachable(p); assertUnreachable(p);
} }
} }
export namespace TaskIdentifiers { export namespace TaskIdentifiers {
export function forWithdrawal(wg: WithdrawalGroupRecord): string { export function forWithdrawal(wg: WithdrawalGroupRecord): TaskId {
return `${PendingTaskType.Withdraw}:${wg.withdrawalGroupId}`; return `${PendingTaskType.Withdraw}:${wg.withdrawalGroupId}` as TaskId;
} }
export function forExchangeUpdate(exch: ExchangeRecord): string { export function forExchangeUpdate(exch: ExchangeRecord): TaskId {
return `${PendingTaskType.ExchangeUpdate}:${exch.baseUrl}`; return `${PendingTaskType.ExchangeUpdate}:${exch.baseUrl}` as TaskId;
} }
export function forExchangeUpdateFromUrl(exchBaseUrl: string): string { export function forExchangeUpdateFromUrl(exchBaseUrl: string): TaskId {
return `${PendingTaskType.ExchangeUpdate}:${exchBaseUrl}`; return `${PendingTaskType.ExchangeUpdate}:${exchBaseUrl}` as TaskId;
} }
export function forExchangeCheckRefresh(exch: ExchangeRecord): string { export function forExchangeCheckRefresh(exch: ExchangeRecord): TaskId {
return `${PendingTaskType.ExchangeCheckRefresh}:${exch.baseUrl}`; return `${PendingTaskType.ExchangeCheckRefresh}:${exch.baseUrl}` as TaskId;
} }
export function forTipPickup(tipRecord: TipRecord): string { export function forTipPickup(tipRecord: TipRecord): TaskId {
return `${PendingTaskType.TipPickup}:${tipRecord.walletTipId}`; return `${PendingTaskType.TipPickup}:${tipRecord.walletTipId}` as TaskId;
} }
export function forRefresh(refreshGroupRecord: RefreshGroupRecord): string { export function forRefresh(refreshGroupRecord: RefreshGroupRecord): TaskId {
return `${PendingTaskType.Refresh}:${refreshGroupRecord.refreshGroupId}`; return `${PendingTaskType.Refresh}:${refreshGroupRecord.refreshGroupId}` as TaskId;
} }
export function forPay(purchaseRecord: PurchaseRecord): string { export function forPay(purchaseRecord: PurchaseRecord): TaskId {
return `${PendingTaskType.Purchase}:${purchaseRecord.proposalId}`; return `${PendingTaskType.Purchase}:${purchaseRecord.proposalId}` as TaskId;
} }
export function forRecoup(recoupRecord: RecoupGroupRecord): string { export function forRecoup(recoupRecord: RecoupGroupRecord): TaskId {
return `${PendingTaskType.Recoup}:${recoupRecord.recoupGroupId}`; return `${PendingTaskType.Recoup}:${recoupRecord.recoupGroupId}` as TaskId;
} }
export function forDeposit(depositRecord: DepositGroupRecord): string { export function forDeposit(depositRecord: DepositGroupRecord): TaskId {
return `${PendingTaskType.Deposit}:${depositRecord.depositGroupId}`; return `${PendingTaskType.Deposit}:${depositRecord.depositGroupId}` as TaskId;
} }
export function forBackup(backupRecord: BackupProviderRecord): string { export function forBackup(backupRecord: BackupProviderRecord): TaskId {
return `${PendingTaskType.Backup}:${backupRecord.baseUrl}`; return `${PendingTaskType.Backup}:${backupRecord.baseUrl}` as TaskId;
} }
export function forPeerPushPaymentInitiation( export function forPeerPushPaymentInitiation(
ppi: PeerPushPaymentInitiationRecord, ppi: PeerPushPaymentInitiationRecord,
): string { ): TaskId {
return `${PendingTaskType.PeerPushDebit}:${ppi.pursePub}`; return `${PendingTaskType.PeerPushDebit}:${ppi.pursePub}` as TaskId;
} }
export function forPeerPullPaymentInitiation( export function forPeerPullPaymentInitiation(
ppi: PeerPullPaymentInitiationRecord, ppi: PeerPullPaymentInitiationRecord,
): string { ): TaskId {
return `${PendingTaskType.PeerPullCredit}:${ppi.pursePub}`; return `${PendingTaskType.PeerPullCredit}:${ppi.pursePub}` as TaskId;
} }
export function forPeerPullPaymentDebit( export function forPeerPullPaymentDebit(
ppi: PeerPullPaymentIncomingRecord, ppi: PeerPullPaymentIncomingRecord,
): string { ): TaskId {
return `${PendingTaskType.PeerPullDebit}:${ppi.peerPullPaymentIncomingId}`; return `${PendingTaskType.PeerPullDebit}:${ppi.peerPullPaymentIncomingId}` as TaskId;
} }
export function forPeerPushCredit( export function forPeerPushCredit(
ppi: PeerPushPaymentIncomingRecord, ppi: PeerPushPaymentIncomingRecord,
): string { ): TaskId {
return `${PendingTaskType.PeerPushCredit}:${ppi.peerPushPaymentIncomingId}`; return `${PendingTaskType.PeerPushCredit}:${ppi.peerPushPaymentIncomingId}` as TaskId;
} }
} }