wallet-core: fix issues with aborting deposits

This commit is contained in:
Florian Dold 2023-05-25 19:11:03 +02:00
parent 485f7d3603
commit 8624d798b7
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
5 changed files with 34 additions and 44 deletions

View File

@ -873,7 +873,7 @@ export function bufferForUint32(n: number): Uint8Array {
* which should be true for all Taler protocol messages. * which should be true for all Taler protocol messages.
*/ */
export function bufferForUint64(n: number): Uint8Array { export function bufferForUint64(n: number): Uint8Array {
const arrBuf = new ArrayBuffer(4); const arrBuf = new ArrayBuffer(8);
const buf = new Uint8Array(arrBuf); const buf = new Uint8Array(arrBuf);
const dv = new DataView(arrBuf); const dv = new DataView(arrBuf);
if (n < 0 || !Number.isInteger(n)) { if (n < 0 || !Number.isInteger(n)) {

View File

@ -232,7 +232,7 @@ export async function resetOperationTimeout(
if (retryRecord) { if (retryRecord) {
// Note that we don't reset the lastError, it should still be visible // Note that we don't reset the lastError, it should still be visible
// while the retry runs. // while the retry runs.
retryRecord.retryInfo = RetryInfo.increment(retryRecord.retryInfo); retryRecord.retryInfo = RetryInfo.reset();
await tx.operationRetries.put(retryRecord); await tx.operationRetries.put(retryRecord);
} }
}); });

View File

@ -89,6 +89,7 @@ import {
import { selectPayCoinsNew } from "../util/coinSelection.js"; import { selectPayCoinsNew } from "../util/coinSelection.js";
import { import {
constructTransactionIdentifier, constructTransactionIdentifier,
notifyTransition,
parseTransactionIdentifier, parseTransactionIdentifier,
stopLongpolling, stopLongpolling,
} from "./transactions.js"; } from "./transactions.js";
@ -158,8 +159,16 @@ export function computeDepositTransactionStatus(
return { return {
major: TransactionMajorState.Suspended, major: TransactionMajorState.Suspended,
}; };
case DepositOperationStatus.Aborting:
return {
major: TransactionMajorState.Aborting,
};
case DepositOperationStatus.Aborted:
return {
major: TransactionMajorState.Aborted,
}
default: default:
throw Error("unexpected deposit group state"); throw Error(`unexpected deposit group state (${dg.operationStatus})`);
} }
} }
@ -379,8 +388,11 @@ async function waitForRefreshOnDepositGroup(
): Promise<OperationAttemptResult> { ): Promise<OperationAttemptResult> {
const abortRefreshGroupId = depositGroup.abortRefreshGroupId; const abortRefreshGroupId = depositGroup.abortRefreshGroupId;
checkLogicInvariant(!!abortRefreshGroupId); checkLogicInvariant(!!abortRefreshGroupId);
// FIXME: Emit notification on state transition! const transactionId = constructTransactionIdentifier({
const res = await ws.db tag: TransactionType.Deposit,
depositGroupId: depositGroup.depositGroupId,
});
const transitionInfo = await ws.db
.mktx((x) => [x.refreshGroups, x.depositGroups]) .mktx((x) => [x.refreshGroups, x.depositGroups])
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId);
@ -405,30 +417,17 @@ async function waitForRefreshOnDepositGroup(
if (!newDg) { if (!newDg) {
return; return;
} }
const oldDepositTxStatus = computeDepositTransactionStatus(newDg); const oldTxState = computeDepositTransactionStatus(newDg);
newDg.operationStatus = newOpState; newDg.operationStatus = newOpState;
const newDepositTxStatus = computeDepositTransactionStatus(newDg); const newTxState = computeDepositTransactionStatus(newDg);
await tx.depositGroups.put(newDg); await tx.depositGroups.put(newDg);
return { oldDepositTxStatus, newDepositTxStatus }; return { oldTxState, newTxState };
} }
return undefined; return undefined;
}); });
if (res) { notifyTransition(ws, transactionId, transitionInfo);
const transactionId = constructTransactionIdentifier({ return OperationAttemptResult.pendingEmpty();
tag: TransactionType.Deposit,
depositGroupId: depositGroup.depositGroupId,
});
ws.notify({
type: NotificationType.TransactionStateTransition,
transactionId,
oldTxState: res.oldDepositTxStatus,
newTxState: res.newDepositTxStatus,
});
return OperationAttemptResult.pendingEmpty();
} else {
return OperationAttemptResult.pendingEmpty();
}
} }
async function refundDepositGroup( async function refundDepositGroup(
@ -436,6 +435,7 @@ async function refundDepositGroup(
depositGroup: DepositGroupRecord, depositGroup: DepositGroupRecord,
): Promise<OperationAttemptResult> { ): Promise<OperationAttemptResult> {
const newTxPerCoin = [...depositGroup.transactionPerCoin]; const newTxPerCoin = [...depositGroup.transactionPerCoin];
logger.info(`status per coin: ${j2s(depositGroup.transactionPerCoin)}`);
for (let i = 0; i < depositGroup.transactionPerCoin.length; i++) { for (let i = 0; i < depositGroup.transactionPerCoin.length; i++) {
const st = depositGroup.transactionPerCoin[i]; const st = depositGroup.transactionPerCoin[i];
switch (st) { switch (st) {
@ -475,6 +475,7 @@ async function refundDepositGroup(
method: "POST", method: "POST",
body: refundReq, body: refundReq,
}); });
logger.info(`coin ${i} refund HTTP status for coin: ${httpResp.status}`);
let newStatus: DepositElementStatus; let newStatus: DepositElementStatus;
if (httpResp.status === 200) { if (httpResp.status === 200) {
// FIXME: validate response // FIXME: validate response
@ -492,7 +493,7 @@ async function refundDepositGroup(
let isDone = true; let isDone = true;
for (let i = 0; i < newTxPerCoin.length; i++) { for (let i = 0; i < newTxPerCoin.length; i++) {
if ( if (
newTxPerCoin[i] != DepositElementStatus.RefundFailed || newTxPerCoin[i] != DepositElementStatus.RefundFailed &&
newTxPerCoin[i] != DepositElementStatus.RefundSuccess newTxPerCoin[i] != DepositElementStatus.RefundSuccess
) { ) {
isDone = false; isDone = false;
@ -535,6 +536,7 @@ async function refundDepositGroup(
await tx.depositGroups.put(newDg); await tx.depositGroups.put(newDg);
}); });
return OperationAttemptResult.pendingEmpty(); return OperationAttemptResult.pendingEmpty();
} }
@ -775,10 +777,13 @@ export async function processDepositGroup(
} }
if (depositGroup.operationStatus === DepositOperationStatus.Aborting) { if (depositGroup.operationStatus === DepositOperationStatus.Aborting) {
logger.info("processing deposit tx in 'aborting'");
const abortRefreshGroupId = depositGroup.abortRefreshGroupId; const abortRefreshGroupId = depositGroup.abortRefreshGroupId;
if (!abortRefreshGroupId) { if (!abortRefreshGroupId) {
logger.info("refunding deposit group");
return refundDepositGroup(ws, depositGroup); return refundDepositGroup(ws, depositGroup);
} }
logger.info("waiting for refresh");
return waitForRefreshOnDepositGroup(ws, depositGroup); return waitForRefreshOnDepositGroup(ws, depositGroup);
} }
return OperationAttemptResult.finishedEmpty(); return OperationAttemptResult.finishedEmpty();

View File

@ -198,7 +198,10 @@ async function gatherDepositPending(
resp: PendingOperationsResponse, resp: PendingOperationsResponse,
): Promise<void> { ): Promise<void> {
const dgs = await tx.depositGroups.indexes.byStatus.getAll( const dgs = await tx.depositGroups.indexes.byStatus.getAll(
OperationStatus.Pending, GlobalIDB.KeyRange.bound(
OperationStatusRange.ACTIVE_START,
OperationStatusRange.ACTIVE_END,
),
); );
for (const dg of dgs) { for (const dg of dgs) {
if (dg.timestampFinished) { if (dg.timestampFinished) {

View File

@ -1307,7 +1307,7 @@ export async function retryTransaction(
ws: InternalWalletState, ws: InternalWalletState,
transactionId: string, transactionId: string,
): Promise<void> { ): Promise<void> {
logger.info(`retrying transaction ${transactionId}`); logger.info(`resetting retry timeout for ${transactionId}`);
const parsedTx = parseTransactionIdentifier(transactionId); const parsedTx = parseTransactionIdentifier(transactionId);
@ -1325,9 +1325,6 @@ export async function retryTransaction(
}); });
await resetOperationTimeout(ws, taskId); await resetOperationTimeout(ws, taskId);
stopLongpolling(ws, taskId); stopLongpolling(ws, taskId);
await runOperationWithErrorReporting(ws, taskId, () =>
processPeerPullCredit(ws, parsedTx.pursePub),
);
break; break;
} }
case TransactionType.Deposit: { case TransactionType.Deposit: {
@ -1337,9 +1334,6 @@ export async function retryTransaction(
}); });
await resetOperationTimeout(ws, taskId); await resetOperationTimeout(ws, taskId);
stopLongpolling(ws, taskId); stopLongpolling(ws, taskId);
await runOperationWithErrorReporting(ws, taskId, () =>
processDepositGroup(ws, parsedTx.depositGroupId),
);
break; break;
} }
case TransactionType.Withdrawal: { case TransactionType.Withdrawal: {
@ -1350,9 +1344,6 @@ export async function retryTransaction(
}); });
await resetOperationTimeout(ws, taskId); await resetOperationTimeout(ws, taskId);
stopLongpolling(ws, taskId); stopLongpolling(ws, taskId);
await runOperationWithErrorReporting(ws, taskId, () =>
processWithdrawalGroup(ws, parsedTx.withdrawalGroupId),
);
break; break;
} }
case TransactionType.Payment: { case TransactionType.Payment: {
@ -1362,9 +1353,6 @@ export async function retryTransaction(
}); });
await resetOperationTimeout(ws, taskId); await resetOperationTimeout(ws, taskId);
stopLongpolling(ws, taskId); stopLongpolling(ws, taskId);
await runOperationWithErrorReporting(ws, taskId, () =>
processPurchasePay(ws, parsedTx.proposalId),
);
break; break;
} }
case TransactionType.Tip: { case TransactionType.Tip: {
@ -1374,9 +1362,6 @@ export async function retryTransaction(
}); });
await resetOperationTimeout(ws, taskId); await resetOperationTimeout(ws, taskId);
stopLongpolling(ws, taskId); stopLongpolling(ws, taskId);
await runOperationWithErrorReporting(ws, taskId, () =>
processTip(ws, parsedTx.walletTipId),
);
break; break;
} }
case TransactionType.Refresh: { case TransactionType.Refresh: {
@ -1386,9 +1371,6 @@ export async function retryTransaction(
}); });
await resetOperationTimeout(ws, taskId); await resetOperationTimeout(ws, taskId);
stopLongpolling(ws, taskId); stopLongpolling(ws, taskId);
await runOperationWithErrorReporting(ws, taskId, () =>
processRefreshGroup(ws, parsedTx.refreshGroupId),
);
break; break;
} }
default: default: