wallet-core: rename OperationAttempt->TaskRun, do not allow task result values anymore

This commit is contained in:
Florian Dold 2023-06-30 16:14:58 +02:00
parent 7523ffa910
commit d4ee961387
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
17 changed files with 284 additions and 364 deletions

View File

@ -1754,6 +1754,7 @@ export interface CoreApiRequestEnvelope {
operation: string;
args: unknown;
}
export type CoreApiResponse = CoreApiResponseSuccess | CoreApiResponseError;
export type CoreApiMessageEnvelope = CoreApiResponse | CoreApiNotification;

View File

@ -94,8 +94,8 @@ import {
} from "../../util/invariants.js";
import { addAttentionRequest, removeAttentionRequest } from "../attention.js";
import {
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResult,
TaskRunResultType,
TaskIdentifiers,
} from "../common.js";
import { checkPaymentByProposalId, preparePayForUri } from "../pay-merchant.js";
@ -250,7 +250,7 @@ function getNextBackupTimestamp(): TalerPreciseTimestamp {
async function runBackupCycleForProvider(
ws: InternalWalletState,
args: BackupForProviderArgs,
): Promise<OperationAttemptResult<unknown, { talerUri?: string }>> {
): Promise<TaskRunResult> {
const provider = await ws.db
.mktx((x) => [x.backupProviders])
.runReadOnly(async (tx) => {
@ -259,10 +259,7 @@ async function runBackupCycleForProvider(
if (!provider) {
logger.warn("provider disappeared");
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
const backupJson = await exportBackup(ws);
@ -333,10 +330,7 @@ async function runBackupCycleForProvider(
type: AttentionType.BackupUnpaid,
});
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
if (resp.status === HttpStatusCode.PaymentRequired) {
@ -378,10 +372,7 @@ async function runBackupCycleForProvider(
});
return {
type: OperationAttemptResultType.Pending,
result: {
talerUri,
},
type: TaskRunResultType.Pending,
};
}
const result = res;
@ -415,10 +406,7 @@ async function runBackupCycleForProvider(
);
return {
type: OperationAttemptResultType.Pending,
result: {
talerUri,
},
type: TaskRunResultType.Pending,
};
}
@ -445,8 +433,7 @@ async function runBackupCycleForProvider(
});
return {
type: OperationAttemptResultType.Finished,
result: undefined,
type: TaskRunResultType.Finished,
};
}
@ -487,7 +474,7 @@ async function runBackupCycleForProvider(
const err = await readTalerErrorResponse(resp);
logger.error(`got error response from backup provider: ${j2s(err)}`);
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: err,
};
}
@ -495,7 +482,7 @@ async function runBackupCycleForProvider(
export async function processBackupForProvider(
ws: InternalWalletState,
backupProviderBaseUrl: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const provider = await ws.db
.mktx((x) => [x.backupProviders])
.runReadOnly(async (tx) => {
@ -720,23 +707,24 @@ async function runFirstBackupCycleForProvider(
): Promise<AddBackupProviderResponse> {
const resp = await runBackupCycleForProvider(ws, args);
switch (resp.type) {
case OperationAttemptResultType.Error:
case TaskRunResultType.Error:
throw TalerError.fromDetail(
TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
resp.errorDetail as any, //FIXME create an error for backup problems
);
case OperationAttemptResultType.Finished:
case TaskRunResultType.Finished:
return {
status: "ok",
};
case OperationAttemptResultType.Longpoll:
case TaskRunResultType.Longpoll:
throw Error(
"unexpected runFirstBackupCycleForProvider result (longpoll)",
);
case OperationAttemptResultType.Pending:
case TaskRunResultType.Pending:
return {
status: "payment-required",
talerUri: resp.result.talerUri,
talerUri: "FIXME",
//talerUri: resp.result.talerUri,
};
default:
assertUnreachable(resp);

View File

@ -433,25 +433,25 @@ async function storePendingTaskFinished(
});
}
export async function runTaskWithErrorReporting<T1, T2>(
export async function runTaskWithErrorReporting(
ws: InternalWalletState,
opId: TaskId,
f: () => Promise<OperationAttemptResult<T1, T2>>,
): Promise<OperationAttemptResult<T1, T2>> {
f: () => Promise<TaskRunResult>,
): Promise<TaskRunResult> {
let maybeError: TalerErrorDetail | undefined;
try {
const resp = await f();
switch (resp.type) {
case OperationAttemptResultType.Error:
case TaskRunResultType.Error:
await storePendingTaskError(ws, opId, resp.errorDetail);
return resp;
case OperationAttemptResultType.Finished:
case TaskRunResultType.Finished:
await storePendingTaskFinished(ws, opId);
return resp;
case OperationAttemptResultType.Pending:
case TaskRunResultType.Pending:
await storePendingTaskPending(ws, opId);
return resp;
case OperationAttemptResultType.Longpoll:
case TaskRunResultType.Longpoll:
return resp;
}
} catch (e) {
@ -459,7 +459,7 @@ export async function runTaskWithErrorReporting<T1, T2>(
if (ws.stopped) {
logger.warn("crypto API stopped during shutdown, ignoring error");
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
{},
@ -474,7 +474,7 @@ export async function runTaskWithErrorReporting<T1, T2>(
maybeError = e.errorDetail;
await storePendingTaskError(ws, opId, maybeError!);
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: e.errorDetail,
};
} else if (e instanceof Error) {
@ -492,7 +492,7 @@ export async function runTaskWithErrorReporting<T1, T2>(
);
await storePendingTaskError(ws, opId, maybeError);
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: maybeError,
};
} else {
@ -504,7 +504,7 @@ export async function runTaskWithErrorReporting<T1, T2>(
);
await storePendingTaskError(ws, opId, maybeError);
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: maybeError,
};
}
@ -654,59 +654,55 @@ export interface TransactionManager {
abort(): Promise<void>;
suspend(): Promise<void>;
resume(): Promise<void>;
process(): Promise<OperationAttemptResult>;
process(): Promise<TaskRunResult>;
}
export enum OperationAttemptResultType {
export enum TaskRunResultType {
Finished = "finished",
Pending = "pending",
Error = "error",
Longpoll = "longpoll",
}
export type OperationAttemptResult<TSuccess = unknown, TPending = unknown> =
| OperationAttemptFinishedResult<TSuccess>
| OperationAttemptErrorResult
| OperationAttemptLongpollResult
| OperationAttemptPendingResult<TPending>;
export type TaskRunResult =
| TaskRunFinishedResult
| TaskRunErrorResult
| TaskRunLongpollResult
| TaskRunPendingResult;
export namespace OperationAttemptResult {
export function finishedEmpty(): OperationAttemptResult<unknown, unknown> {
export namespace TaskRunResult {
export function finished(): TaskRunResult {
return {
type: OperationAttemptResultType.Finished,
result: undefined,
type: TaskRunResultType.Finished,
};
}
export function pendingEmpty(): OperationAttemptResult<unknown, unknown> {
export function pending(): TaskRunResult {
return {
type: OperationAttemptResultType.Pending,
result: undefined,
type: TaskRunResultType.Pending,
};
}
export function longpoll(): OperationAttemptResult<unknown, unknown> {
export function longpoll(): TaskRunResult {
return {
type: OperationAttemptResultType.Longpoll,
type: TaskRunResultType.Longpoll,
};
}
}
export interface OperationAttemptFinishedResult<T> {
type: OperationAttemptResultType.Finished;
result: T;
export interface TaskRunFinishedResult {
type: TaskRunResultType.Finished;
}
export interface OperationAttemptPendingResult<T> {
type: OperationAttemptResultType.Pending;
result: T;
export interface TaskRunPendingResult {
type: TaskRunResultType.Pending;
}
export interface OperationAttemptErrorResult {
type: OperationAttemptResultType.Error;
export interface TaskRunErrorResult {
type: TaskRunResultType.Error;
errorDetail: TalerErrorDetail;
}
export interface OperationAttemptLongpollResult {
type: OperationAttemptResultType.Longpoll;
export interface TaskRunLongpollResult {
type: TaskRunResultType.Longpoll;
}
export interface RetryInfo {
@ -942,19 +938,3 @@ export namespace TaskIdentifiers {
return `${PendingTaskType.PeerPushCredit}:${ppi.peerPushPaymentIncomingId}` as TaskId;
}
}
/**
* Run an operation handler, expect a success result and extract the success value.
*/
export async function unwrapOperationHandlerResultOrThrow<T>(
res: OperationAttemptResult<T>,
): Promise<T> {
switch (res.type) {
case OperationAttemptResultType.Finished:
return res.result;
case OperationAttemptResultType.Error:
throw TalerError.fromUncheckedDetail(res.errorDetail);
default:
throw Error(`unexpected operation result (${res.type})`);
}
}

View File

@ -81,7 +81,7 @@ import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import {
constructTaskIdentifier,
OperationAttemptResult,
TaskRunResult,
runLongpollAsync,
spendCoins,
TombstoneTag,
@ -462,7 +462,7 @@ async function checkDepositKycStatus(
async function waitForRefreshOnDepositGroup(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const abortRefreshGroupId = depositGroup.abortRefreshGroupId;
checkLogicInvariant(!!abortRefreshGroupId);
const transactionId = constructTransactionIdentifier({
@ -503,13 +503,13 @@ async function waitForRefreshOnDepositGroup(
});
notifyTransition(ws, transactionId, transitionInfo);
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
async function refundDepositGroup(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const newTxPerCoin = [...depositGroup.transactionPerCoin];
logger.info(`status per coin: ${j2s(depositGroup.transactionPerCoin)}`);
for (let i = 0; i < depositGroup.transactionPerCoin.length; i++) {
@ -614,13 +614,13 @@ async function refundDepositGroup(
await tx.depositGroups.put(newDg);
});
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
async function processDepositGroupAborting(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
logger.info("processing deposit tx in 'aborting'");
const abortRefreshGroupId = depositGroup.abortRefreshGroupId;
if (!abortRefreshGroupId) {
@ -634,7 +634,7 @@ async function processDepositGroupAborting(
async function processDepositGroupPendingKyc(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const { depositGroupId } = depositGroup;
const transactionId = constructTransactionIdentifier({
tag: TransactionType.Deposit,
@ -696,7 +696,7 @@ async function processDepositGroupPendingKyc(
);
}
});
return OperationAttemptResult.longpoll();
return TaskRunResult.longpoll();
}
/**
@ -709,7 +709,7 @@ async function transitionToKycRequired(
depositGroup: DepositGroupRecord,
kycInfo: KycPendingInfo,
exchangeUrl: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const { depositGroupId } = depositGroup;
const userType = "individual";
@ -728,7 +728,7 @@ async function transitionToKycRequired(
});
if (kycStatusReq.status === HttpStatusCode.Ok) {
logger.warn("kyc requested, but already fulfilled");
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
} else if (kycStatusReq.status === HttpStatusCode.Accepted) {
const kycStatus = await kycStatusReq.json();
logger.info(`kyc status: ${j2s(kycStatus)}`);
@ -754,7 +754,7 @@ async function transitionToKycRequired(
return { oldTxState, newTxState };
});
notifyTransition(ws, transactionId, transitionInfo);
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
} else {
throw Error(`unexpected response from kyc-check (${kycStatusReq.status})`);
}
@ -764,7 +764,7 @@ async function processDepositGroupPendingTrack(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
cancellationToken?: CancellationToken,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const { depositGroupId } = depositGroup;
for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) {
const coinPub = depositGroup.payCoinSelection.coinPubs[i];
@ -905,10 +905,10 @@ async function processDepositGroupPendingTrack(
});
notifyTransition(ws, transactionId, transitionInfo);
if (allWired) {
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
} else {
// FIXME: Use long-polling.
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
}
@ -916,7 +916,7 @@ async function processDepositGroupPendingDeposit(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
cancellationToken?: CancellationToken,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
logger.info("processing deposit group in pending(deposit)");
const depositGroupId = depositGroup.depositGroupId;
const contractData = extractContractData(
@ -1000,7 +1000,7 @@ async function processDepositGroupPendingDeposit(
});
notifyTransition(ws, transactionId, transitionInfo);
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
/**
@ -1012,7 +1012,7 @@ export async function processDepositGroup(
options: {
cancellationToken?: CancellationToken;
} = {},
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const depositGroup = await ws.db
.mktx((x) => [x.depositGroups])
.runReadOnly(async (tx) => {
@ -1020,7 +1020,7 @@ export async function processDepositGroup(
});
if (!depositGroup) {
logger.warn(`deposit group ${depositGroupId} not found`);
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
switch (depositGroup.operationStatus) {
@ -1042,7 +1042,7 @@ export async function processDepositGroup(
return processDepositGroupAborting(ws, depositGroup);
}
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
async function getExchangeWireFee(

View File

@ -76,11 +76,10 @@ import {
} from "../util/query.js";
import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "../versions.js";
import {
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResultType,
runTaskWithErrorReporting,
TaskIdentifiers,
unwrapOperationHandlerResultOrThrow,
TaskRunResult,
} from "./common.js";
const logger = new Logger("exchanges.ts");
@ -559,13 +558,34 @@ export async function updateExchangeFromUrl(
exchangeDetails: ExchangeDetailsRecord;
}> {
const canonUrl = canonicalizeBaseUrl(baseUrl);
return unwrapOperationHandlerResultOrThrow(
await runTaskWithErrorReporting(
ws,
TaskIdentifiers.forExchangeUpdateFromUrl(canonUrl),
() => updateExchangeFromUrlHandler(ws, canonUrl, options),
),
const res = await runTaskWithErrorReporting(
ws,
TaskIdentifiers.forExchangeUpdateFromUrl(canonUrl),
() => updateExchangeFromUrlHandler(ws, canonUrl, options),
);
switch (res.type) {
case TaskRunResultType.Finished: {
const now = AbsoluteTime.now();
const { exchange, exchangeDetails } = await ws.db
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadWrite(async (tx) => {
let exchange = await tx.exchanges.get(canonUrl);
const exchangeDetails = await getExchangeDetails(tx, baseUrl);
return { exchange, exchangeDetails };
});
if (!exchange) {
throw Error("exchange not found");
}
if (!exchangeDetails) {
throw Error("exchange details not found");
}
return { exchange, exchangeDetails };
}
case TaskRunResultType.Error:
throw TalerError.fromUncheckedDetail(res.errorDetail);
default:
throw Error(`unexpected operation result (${res.type})`);
}
}
/**
@ -581,12 +601,7 @@ export async function updateExchangeFromUrlHandler(
forceNow?: boolean;
cancellationToken?: CancellationToken;
} = {},
): Promise<
OperationAttemptResult<{
exchange: ExchangeRecord;
exchangeDetails: ExchangeDetailsRecord;
}>
> {
): Promise<TaskRunResult> {
const forceNow = options.forceNow ?? false;
logger.trace(
`updating exchange info for ${exchangeBaseUrl}, forced: ${forceNow}`,
@ -620,10 +635,7 @@ export async function updateExchangeFromUrlHandler(
}
}
return {
type: OperationAttemptResultType.Finished,
result: { exchange, exchangeDetails },
};
return TaskRunResult.finished();
}
logger.info("updating exchange /keys info");
@ -679,7 +691,7 @@ export async function updateExchangeFromUrlHandler(
},
);
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail,
};
}
@ -911,13 +923,7 @@ export async function updateExchangeFromUrlHandler(
});
}
return {
type: OperationAttemptResultType.Finished,
result: {
exchange: updated.exchange,
exchangeDetails: updated.exchangeDetails,
},
};
return TaskRunResult.finished();
}
/**

View File

@ -112,8 +112,8 @@ import { checkDbInvariant } from "../util/invariants.js";
import { GetReadOnlyAccess } from "../util/query.js";
import {
constructTaskIdentifier,
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResult,
TaskRunResultType,
RetryInfo,
TaskIdentifiers,
} from "./common.js";
@ -325,7 +325,7 @@ export function extractContractData(
async function processDownloadProposal(
ws: InternalWalletState,
proposalId: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const proposal = await ws.db
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
@ -333,17 +333,11 @@ async function processDownloadProposal(
});
if (!proposal) {
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
if (proposal.purchaseStatus != PurchaseStatus.PendingDownloadingProposal) {
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
const transactionId = constructTransactionIdentifier({
@ -560,10 +554,7 @@ async function processDownloadProposal(
notifyTransition(ws, transactionId, transitionInfo);
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
/**
@ -1065,7 +1056,7 @@ export async function checkPaymentByProposalId(
notifyTransition(ws, transactionId, transitionInfo);
// FIXME: What about error handling?! This doesn't properly store errors in the DB.
const r = await processPurchasePay(ws, proposalId, { forceNow: true });
if (r.type !== OperationAttemptResultType.Finished) {
if (r.type !== TaskRunResultType.Finished) {
// FIXME: This does not surface the original error
throw Error("submitting pay failed");
}
@ -1253,7 +1244,7 @@ export async function runPayForConfirmPay(
});
logger.trace(`processPurchasePay response type ${res.type}`);
switch (res.type) {
case OperationAttemptResultType.Finished: {
case TaskRunResultType.Finished: {
const purchase = await ws.db
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
@ -1272,7 +1263,7 @@ export async function runPayForConfirmPay(
}),
};
}
case OperationAttemptResultType.Error: {
case TaskRunResultType.Error: {
// We hide transient errors from the caller.
const opRetry = await ws.db
.mktx((x) => [x.operationRetries])
@ -1286,7 +1277,7 @@ export async function runPayForConfirmPay(
}),
};
}
case OperationAttemptResultType.Pending:
case TaskRunResultType.Pending:
logger.trace("reporting pending as confirmPay response");
return {
type: ConfirmPayResultType.Pending,
@ -1296,7 +1287,7 @@ export async function runPayForConfirmPay(
}),
lastError: undefined,
};
case OperationAttemptResultType.Longpoll:
case TaskRunResultType.Longpoll:
throw Error("unexpected processPurchasePay result (longpoll)");
default:
assertUnreachable(res);
@ -1456,7 +1447,7 @@ export async function confirmPay(
export async function processPurchase(
ws: InternalWalletState,
proposalId: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const purchase = await ws.db
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
@ -1464,7 +1455,7 @@ export async function processPurchase(
});
if (!purchase) {
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: {
// FIXME: allocate more specific error code
code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
@ -1504,10 +1495,7 @@ export async function processPurchase(
case PurchaseStatus.SuspendedQueryingAutoRefund:
case PurchaseStatus.SuspendedQueryingRefund:
case PurchaseStatus.FailedAbort:
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
default:
assertUnreachable(purchase.purchaseStatus);
// throw Error(`unexpected purchase status (${purchase.purchaseStatus})`);
@ -1518,7 +1506,7 @@ export async function processPurchasePay(
ws: InternalWalletState,
proposalId: string,
options: unknown = {},
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const purchase = await ws.db
.mktx((x) => [x.purchases])
.runReadOnly(async (tx) => {
@ -1526,7 +1514,7 @@ export async function processPurchasePay(
});
if (!purchase) {
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: {
// FIXME: allocate more specific error code
code: TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION,
@ -1541,7 +1529,7 @@ export async function processPurchasePay(
case PurchaseStatus.PendingPayingReplay:
break;
default:
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
logger.trace(`processing purchase pay ${proposalId}`);
@ -1589,7 +1577,7 @@ export async function processPurchasePay(
if (resp.status >= 500 && resp.status <= 599) {
const errDetails = await readUnexpectedResponseDetails(resp);
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_PAY_MERCHANT_SERVER_ERROR,
{
@ -1613,10 +1601,7 @@ export async function processPurchasePay(
// FIXME: Should we really consider this to be pending?
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
return TaskRunResult.pending();
}
}
@ -1677,7 +1662,7 @@ export async function processPurchasePay(
await unblockBackup(ws, proposalId);
}
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
export async function refuseProposal(
@ -2114,7 +2099,7 @@ export function computePayMerchantTransactionActions(
async function processPurchaseAutoRefund(
ws: InternalWalletState,
purchase: PurchaseRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const proposalId = purchase.proposalId;
logger.trace(`processing auto-refund for proposal ${proposalId}`);
@ -2130,7 +2115,7 @@ async function processPurchaseAutoRefund(
// FIXME: Put this logic into runLongpollAsync?
if (ws.activeLongpoll[taskId]) {
return OperationAttemptResult.longpoll();
return TaskRunResult.longpoll();
}
const download = await expectProposalDownload(ws, purchase);
@ -2215,13 +2200,13 @@ async function processPurchaseAutoRefund(
}
});
return OperationAttemptResult.longpoll();
return TaskRunResult.longpoll();
}
async function processPurchaseAbortingRefund(
ws: InternalWalletState,
purchase: PurchaseRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const proposalId = purchase.proposalId;
const download = await expectProposalDownload(ws, purchase);
logger.trace(`processing aborting-refund for proposal ${proposalId}`);
@ -2296,7 +2281,7 @@ async function processPurchaseAbortingRefund(
async function processPurchaseQueryRefund(
ws: InternalWalletState,
purchase: PurchaseRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const proposalId = purchase.proposalId;
logger.trace(`processing query-refund for proposal ${proposalId}`);
@ -2341,7 +2326,7 @@ async function processPurchaseQueryRefund(
return { oldTxState, newTxState };
});
notifyTransition(ws, transactionId, transitionInfo);
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
} else {
const refundAwaiting = Amounts.sub(
Amounts.parseOrThrow(orderStatus.refund_amount),
@ -2367,14 +2352,14 @@ async function processPurchaseQueryRefund(
return { oldTxState, newTxState };
});
notifyTransition(ws, transactionId, transitionInfo);
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
}
async function processPurchaseAcceptRefund(
ws: InternalWalletState,
purchase: PurchaseRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const proposalId = purchase.proposalId;
const download = await expectProposalDownload(ws, purchase);
@ -2472,7 +2457,7 @@ async function storeRefunds(
purchase: PurchaseRecord,
refunds: MerchantCoinRefundStatus[],
reason: RefundReason,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
logger.info(`storing refunds: ${j2s(refunds)}`);
const transactionId = constructTransactionIdentifier({
@ -2699,16 +2684,16 @@ async function storeRefunds(
});
if (!result) {
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
notifyTransition(ws, transactionId, result.transitionInfo);
if (result.numPendingItemsTotal > 0) {
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
export function computeRefundTransactionState(

View File

@ -66,8 +66,8 @@ import { assertUnreachable } from "../util/assertUnreachable.js";
import { checkDbInvariant } from "../util/invariants.js";
import {
LongpollResult,
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResult,
TaskRunResultType,
constructTaskIdentifier,
runLongpollAsync,
} from "./common.js";
@ -184,7 +184,7 @@ async function longpollKycStatus(
exchangeUrl: string,
kycInfo: KycPendingInfo,
userType: KycUserType,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullCredit,
pursePub,
@ -242,14 +242,14 @@ async function longpollKycStatus(
}
});
return {
type: OperationAttemptResultType.Longpoll,
type: TaskRunResultType.Longpoll,
};
}
async function processPeerPullCreditAbortingDeletePurse(
ws: InternalWalletState,
peerPullIni: PeerPullPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const { pursePub, pursePriv } = peerPullIni;
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushDebit,
@ -296,13 +296,13 @@ async function processPeerPullCreditAbortingDeletePurse(
});
notifyTransition(ws, transactionId, transitionInfo);
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
async function handlePeerPullCreditWithdrawing(
ws: InternalWalletState,
pullIni: PeerPullPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
if (!pullIni.withdrawalGroupId) {
throw Error("invalid db state (withdrawing, but no withdrawal group ID");
}
@ -346,17 +346,17 @@ async function handlePeerPullCreditWithdrawing(
});
notifyTransition(ws, transactionId, transitionInfo);
if (finished) {
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
} else {
// FIXME: Return indicator that we depend on the other operation!
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
}
async function handlePeerPullCreditCreatePurse(
ws: InternalWalletState,
pullIni: PeerPullPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount));
const pursePub = pullIni.pursePub;
const mergeReserve = await ws.db
@ -447,16 +447,13 @@ async function handlePeerPullCreditCreatePurse(
await tx.peerPullPaymentInitiations.put(pi2);
});
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
export async function processPeerPullCredit(
ws: InternalWalletState,
pursePub: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const pullIni = await ws.db
.mktx((x) => [x.peerPullPaymentInitiations])
.runReadOnly(async (tx) => {
@ -475,7 +472,7 @@ export async function processPeerPullCredit(
if (ws.activeLongpoll[retryTag]) {
logger.info("peer-pull-credit already in long-polling, returning!");
return {
type: OperationAttemptResultType.Longpoll,
type: TaskRunResultType.Longpoll,
};
}
@ -483,10 +480,7 @@ export async function processPeerPullCredit(
switch (pullIni.status) {
case PeerPullPaymentInitiationStatus.Done: {
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
case PeerPullPaymentInitiationStatus.PendingReady:
runLongpollAsync(ws, retryTag, async (cancellationToken) =>
@ -496,7 +490,7 @@ export async function processPeerPullCredit(
"returning early from processPeerPullCredit for long-polling in background",
);
return {
type: OperationAttemptResultType.Longpoll,
type: TaskRunResultType.Longpoll,
};
case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: {
if (!pullIni.kycInfo) {
@ -528,14 +522,14 @@ export async function processPeerPullCredit(
assertUnreachable(pullIni.status);
}
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
async function processPeerPullCreditKycRequired(
ws: InternalWalletState,
peerIni: PeerPullPaymentInitiationRecord,
kycPending: WalletKycUuid,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullCredit,
pursePub: peerIni.pursePub,
@ -560,10 +554,7 @@ async function processPeerPullCreditKycRequired(
kycStatusRes.status === HttpStatusCode.NoContent
) {
logger.warn("kyc requested, but already fulfilled");
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
} else if (kycStatusRes.status === HttpStatusCode.Accepted) {
const kycStatus = await kycStatusRes.json();
logger.info(`kyc status: ${j2s(kycStatus)}`);
@ -574,7 +565,7 @@ async function processPeerPullCreditKycRequired(
if (!peerInc) {
return {
transitionInfo: undefined,
result: OperationAttemptResult.finishedEmpty(),
result: TaskRunResult.finished(),
};
}
const oldTxState = computePeerPullCreditTransactionState(peerInc);
@ -589,8 +580,8 @@ async function processPeerPullCreditKycRequired(
await tx.peerPullPaymentInitiations.put(peerInc);
// We'll remove this eventually! New clients should rely on the
// kycUrl field of the transaction, not the error code.
const res: OperationAttemptResult = {
type: OperationAttemptResultType.Error,
const res: TaskRunResult = {
type: TaskRunResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
{
@ -604,10 +595,7 @@ async function processPeerPullCreditKycRequired(
};
});
notifyTransition(ws, transactionId, transitionInfo);
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
return TaskRunResult.pending();
} else {
throw Error(`unexpected response from kyc-check (${kycStatusRes.status})`);
}

View File

@ -62,8 +62,8 @@ import {
import { assertUnreachable } from "../util/assertUnreachable.js";
import { checkLogicInvariant } from "../util/invariants.js";
import {
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResult,
TaskRunResultType,
TaskIdentifiers,
constructTaskIdentifier,
runTaskWithErrorReporting,
@ -89,12 +89,12 @@ async function handlePurseCreationConflict(
ws: InternalWalletState,
peerPullInc: PeerPullPaymentIncomingRecord,
resp: HttpResponse,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const pursePub = peerPullInc.pursePub;
const errResp = await readTalerErrorResponse(resp);
if (errResp.code !== TalerErrorCode.EXCHANGE_GENERIC_INSUFFICIENT_FUNDS) {
await failPeerPullDebitTransaction(ws, pursePub);
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
// FIXME: Properly parse!
@ -167,13 +167,13 @@ async function handlePurseCreationConflict(
}
await tx.peerPullPaymentIncoming.put(myPpi);
});
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
async function processPeerPullDebitPendingDeposit(
ws: InternalWalletState,
peerPullInc: PeerPullPaymentIncomingRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId;
const pursePub = peerPullInc.pursePub;
@ -299,21 +299,18 @@ async function processPeerPullDebitPendingDeposit(
default: {
const errResp = await readTalerErrorResponse(httpResp);
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: errResp,
};
}
}
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
async function processPeerPullDebitAbortingRefresh(
ws: InternalWalletState,
peerPullInc: PeerPullPaymentIncomingRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId;
const abortRefreshGroupId = peerPullInc.abortRefreshGroupId;
checkLogicInvariant(!!abortRefreshGroupId);
@ -357,13 +354,13 @@ async function processPeerPullDebitAbortingRefresh(
});
notifyTransition(ws, transactionId, transitionInfo);
// FIXME: Shouldn't this be finished in some cases?!
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
export async function processPeerPullDebit(
ws: InternalWalletState,
peerPullPaymentIncomingId: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const peerPullInc = await ws.db
.mktx((x) => [x.peerPullPaymentIncoming])
.runReadOnly(async (tx) => {
@ -379,10 +376,7 @@ export async function processPeerPullDebit(
case PeerPullDebitRecordStatus.AbortingRefresh:
return await processPeerPullDebitAbortingRefresh(ws, peerPullInc);
}
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
export async function confirmPeerPullDebit(

View File

@ -62,8 +62,8 @@ import {
import { assertUnreachable } from "../util/assertUnreachable.js";
import { checkDbInvariant } from "../util/invariants.js";
import {
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResult,
TaskRunResultType,
constructTaskIdentifier,
runLongpollAsync,
} from "./common.js";
@ -233,7 +233,7 @@ async function longpollKycStatus(
exchangeUrl: string,
kycInfo: KycPendingInfo,
userType: KycUserType,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
peerPushPaymentIncomingId,
@ -293,7 +293,7 @@ async function longpollKycStatus(
}
});
return {
type: OperationAttemptResultType.Longpoll,
type: TaskRunResultType.Longpoll,
};
}
@ -301,7 +301,7 @@ async function processPeerPushCreditKycRequired(
ws: InternalWalletState,
peerInc: PeerPushPaymentIncomingRecord,
kycPending: WalletKycUuid,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId,
@ -326,10 +326,7 @@ async function processPeerPushCreditKycRequired(
kycStatusRes.status === HttpStatusCode.NoContent
) {
logger.warn("kyc requested, but already fulfilled");
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
} else if (kycStatusRes.status === HttpStatusCode.Accepted) {
const kycStatus = await kycStatusRes.json();
logger.info(`kyc status: ${j2s(kycStatus)}`);
@ -342,7 +339,7 @@ async function processPeerPushCreditKycRequired(
if (!peerInc) {
return {
transitionInfo: undefined,
result: OperationAttemptResult.finishedEmpty(),
result: TaskRunResult.finished(),
};
}
const oldTxState = computePeerPushCreditTransactionState(peerInc);
@ -356,8 +353,8 @@ async function processPeerPushCreditKycRequired(
await tx.peerPushPaymentIncoming.put(peerInc);
// We'll remove this eventually! New clients should rely on the
// kycUrl field of the transaction, not the error code.
const res: OperationAttemptResult = {
type: OperationAttemptResultType.Error,
const res: TaskRunResult = {
type: TaskRunResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
{
@ -381,7 +378,7 @@ async function handlePendingMerge(
ws: InternalWalletState,
peerInc: PeerPushPaymentIncomingRecord,
contractTerms: PeerContractTerms,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const { peerPushPaymentIncomingId } = peerInc;
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
@ -506,16 +503,13 @@ async function handlePendingMerge(
);
notifyTransition(ws, transactionId, txRes?.peerPushCreditTransition);
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
async function handlePendingWithdrawing(
ws: InternalWalletState,
peerInc: PeerPushPaymentIncomingRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
if (!peerInc.withdrawalGroupId) {
throw Error("invalid db state (withdrawing, but no withdrawal group ID");
}
@ -561,17 +555,17 @@ async function handlePendingWithdrawing(
});
notifyTransition(ws, transactionId, transitionInfo);
if (finished) {
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
} else {
// FIXME: Return indicator that we depend on the other operation!
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
}
export async function processPeerPushCredit(
ws: InternalWalletState,
peerPushPaymentIncomingId: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
let peerInc: PeerPushPaymentIncomingRecord | undefined;
let contractTerms: PeerContractTerms | undefined;
await ws.db
@ -617,7 +611,7 @@ export async function processPeerPushCredit(
return handlePendingWithdrawing(ws, peerInc);
default:
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
}

View File

@ -61,8 +61,8 @@ import { PendingTaskType } from "../pending-types.js";
import { assertUnreachable } from "../util/assertUnreachable.js";
import { checkLogicInvariant } from "../util/invariants.js";
import {
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResult,
TaskRunResultType,
constructTaskIdentifier,
runLongpollAsync,
spendCoins,
@ -110,12 +110,12 @@ async function handlePurseCreationConflict(
ws: InternalWalletState,
peerPushInitiation: PeerPushPaymentInitiationRecord,
resp: HttpResponse,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const pursePub = peerPushInitiation.pursePub;
const errResp = await readTalerErrorResponse(resp);
if (errResp.code !== TalerErrorCode.EXCHANGE_GENERIC_INSUFFICIENT_FUNDS) {
await failPeerPushDebitTransaction(ws, pursePub);
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
// FIXME: Properly parse!
@ -176,13 +176,13 @@ async function handlePurseCreationConflict(
}
await tx.peerPushPaymentInitiations.put(myPpi);
});
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
async function processPeerPushDebitCreateReserve(
ws: InternalWalletState,
peerPushInitiation: PeerPushPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
logger.info("processing peer-push-debit pending(create-reserve)");
const pursePub = peerPushInitiation.pursePub;
const purseExpiration = peerPushInitiation.purseExpiration;
@ -264,7 +264,7 @@ async function processPeerPushDebitCreateReserve(
case HttpStatusCode.Forbidden: {
// FIXME: Store this error!
await failPeerPushDebitTransaction(ws, pursePub);
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
case HttpStatusCode.Conflict: {
// Handle double-spending
@ -273,7 +273,7 @@ async function processPeerPushDebitCreateReserve(
default: {
const errResp = await readTalerErrorResponse(httpResp);
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: errResp,
};
}
@ -289,13 +289,13 @@ async function processPeerPushDebitCreateReserve(
stTo: PeerPushPaymentInitiationStatus.PendingReady,
});
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
async function processPeerPushDebitAbortingDeletePurse(
ws: InternalWalletState,
peerPushInitiation: PeerPushPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const { pursePub, pursePriv } = peerPushInitiation;
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushDebit,
@ -364,7 +364,7 @@ async function processPeerPushDebitAbortingDeletePurse(
});
notifyTransition(ws, transactionId, transitionInfo);
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
interface SimpleTransition {
@ -406,7 +406,7 @@ async function transitionPeerPushDebitTransaction(
async function processPeerPushDebitAbortingRefresh(
ws: InternalWalletState,
peerPushInitiation: PeerPushPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const pursePub = peerPushInitiation.pursePub;
const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId;
checkLogicInvariant(!!abortRefreshGroupId);
@ -448,7 +448,7 @@ async function processPeerPushDebitAbortingRefresh(
});
notifyTransition(ws, transactionId, transitionInfo);
// FIXME: Shouldn't this be finished in some cases?!
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
}
/**
@ -457,7 +457,7 @@ async function processPeerPushDebitAbortingRefresh(
async function processPeerPushDebitReady(
ws: InternalWalletState,
peerPushInitiation: PeerPushPaymentInitiationRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
logger.info("processing peer-push-debit pending(ready)");
const pursePub = peerPushInitiation.pursePub;
const retryTag = constructTaskIdentifier({
@ -520,14 +520,14 @@ async function processPeerPushDebitReady(
"returning early from peer-push-debit for long-polling in background",
);
return {
type: OperationAttemptResultType.Longpoll,
type: TaskRunResultType.Longpoll,
};
}
export async function processPeerPushDebit(
ws: InternalWalletState,
pursePub: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const peerPushInitiation = await ws.db
.mktx((x) => [x.peerPushPaymentInitiations])
.runReadOnly(async (tx) => {
@ -546,7 +546,7 @@ export async function processPeerPushDebit(
if (ws.activeLongpoll[retryTag]) {
logger.info("peer-push-debit task already in long-polling, returning!");
return {
type: OperationAttemptResultType.Longpoll,
type: TaskRunResultType.Longpoll,
};
}
@ -567,10 +567,7 @@ export async function processPeerPushDebit(
}
}
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
/**

View File

@ -55,7 +55,7 @@ import { checkDbInvariant } from "../util/invariants.js";
import { GetReadWriteAccess } from "../util/query.js";
import { createRefreshGroup, processRefreshGroup } from "./refresh.js";
import { internalCreateWithdrawalGroup } from "./withdraw.js";
import { OperationAttemptResult } from "./common.js";
import { TaskRunResult } from "./common.js";
const logger = new Logger("operations/recoup.ts");
@ -289,18 +289,18 @@ async function recoupRefreshCoin(
export async function processRecoupGroup(
ws: InternalWalletState,
recoupGroupId: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
let recoupGroup = await ws.db
.mktx((x) => [x.recoupGroups])
.runReadOnly(async (tx) => {
return tx.recoupGroups.get(recoupGroupId);
});
if (!recoupGroup) {
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
if (recoupGroup.timestampFinished) {
logger.trace("recoup group finished");
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
const ps = recoupGroup.coinPubs.map(async (x, i) => {
try {
@ -318,12 +318,12 @@ export async function processRecoupGroup(
return tx.recoupGroups.get(recoupGroupId);
});
if (!recoupGroup) {
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
for (const b of recoupGroup.recoupFinishedPerCoin) {
if (!b) {
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
}
@ -408,7 +408,7 @@ export async function processRecoupGroup(
}
await tx.recoupGroups.put(rg2);
});
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
export async function createRecoupGroup(

View File

@ -89,8 +89,8 @@ import {
constructTaskIdentifier,
makeCoinAvailable,
makeCoinsVisible,
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResult,
TaskRunResultType,
} from "./common.js";
import { updateExchangeFromUrl } from "./exchanges.js";
import {
@ -770,23 +770,17 @@ export async function processRefreshGroup(
ws: InternalWalletState,
refreshGroupId: string,
options: Record<string, never> = {},
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
logger.info(`processing refresh group ${refreshGroupId}`);
const refreshGroup = await ws.db
.mktx((x) => [x.refreshGroups])
.runReadOnly(async (tx) => tx.refreshGroups.get(refreshGroupId));
if (!refreshGroup) {
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished()
}
if (refreshGroup.timestampFinished) {
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
// Process refresh sessions of the group in parallel.
logger.trace("processing refresh sessions for old coins");
@ -823,14 +817,11 @@ export async function processRefreshGroup(
logger.warn(`exception: ${e}`);
}
if (inShutdown) {
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
return TaskRunResult.pending();
}
if (errors.length > 0) {
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_REFRESH_GROUP_INCOMPLETE,
{
@ -841,10 +832,7 @@ export async function processRefreshGroup(
};
}
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.pending();
}
async function processRefreshSession(
@ -1122,7 +1110,7 @@ function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime {
export async function autoRefresh(
ws: InternalWalletState,
exchangeBaseUrl: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
logger.info(`doing auto-refresh check for '${exchangeBaseUrl}'`);
// We must make sure that the exchange is up-to-date so that
@ -1204,7 +1192,7 @@ export async function autoRefresh(
AbsoluteTime.toPreciseTimestamp(minCheckThreshold);
await tx.exchanges.put(exchange);
});
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
export function computeRefreshTransactionState(

View File

@ -24,6 +24,7 @@ import {
Duration,
IntegrationTestV2Args,
Logger,
NotificationType,
stringToBytes,
TestPayResult,
WithdrawTestBalanceRequest,
@ -64,6 +65,7 @@ import {
confirmPeerPushCredit,
} from "./pay-peer-push-credit.js";
import { initiatePeerPushDebit } from "./pay-peer-push-debit.js";
import { OpenedPromise, openPromise } from "../index.js";
const logger = new Logger("operations/testing.ts");
@ -445,6 +447,18 @@ export async function runIntegrationTest(
logger.trace("integration test: all done!");
}
async function waitUntilDone(ws: InternalWalletState): Promise<void> {
let p: OpenedPromise<void> | undefined = undefined;
ws.addNotificationListener((notif) => {
if (!p) {
return;
}
if (notif.type === NotificationType.TransactionStateTransition) {
p.resolve();
}
});
}
export async function runIntegrationTest2(
ws: InternalWalletState,
args: IntegrationTestV2Args,

View File

@ -62,8 +62,8 @@ import {
constructTaskIdentifier,
makeCoinAvailable,
makeCoinsVisible,
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResult,
TaskRunResultType,
} from "./common.js";
import { updateExchangeFromUrl } from "./exchanges.js";
import {
@ -241,17 +241,14 @@ export async function prepareTip(
export async function processTip(
ws: InternalWalletState,
walletTipId: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const tipRecord = await ws.db
.mktx((x) => [x.tips])
.runReadOnly(async (tx) => {
return tx.tips.get(walletTipId);
});
if (!tipRecord) {
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
switch (tipRecord.status) {
@ -259,10 +256,7 @@ export async function processTip(
case TipRecordStatus.DialogAccept:
case TipRecordStatus.Done:
case TipRecordStatus.SuspendidPickup:
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
const transactionId = constructTransactionIdentifier({
@ -324,7 +318,7 @@ export async function processTip(
logger.trace(`got transient tip error`);
// FIXME: wrap in another error code that indicates a transient error
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
getHttpResponseErrorDetails(merchantResp),
@ -376,7 +370,7 @@ export async function processTip(
if (!isValid) {
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_TIPPING_COIN_SIGNATURE_INVALID,
{},
@ -430,10 +424,7 @@ export async function processTip(
notifyTransition(ws, transactionId, transitionInfo);
ws.notify({ type: NotificationType.BalanceChange });
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
export async function acceptTip(

View File

@ -90,8 +90,8 @@ import {
} from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js";
import {
OperationAttemptResult,
OperationAttemptResultType,
TaskRunResult,
TaskRunResultType,
TaskIdentifiers,
constructTaskIdentifier,
makeCoinAvailable,
@ -1326,7 +1326,7 @@ export interface WithdrawalGroupContext {
async function processWithdrawalGroupAbortingBank(
ws: InternalWalletState,
withdrawalGroup: WithdrawalGroupRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const { withdrawalGroupId } = withdrawalGroup;
const transactionId = constructTransactionIdentifier({
tag: TransactionType.Withdrawal,
@ -1363,10 +1363,7 @@ async function processWithdrawalGroupAbortingBank(
};
});
notifyTransition(ws, transactionId, transitionInfo);
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
/**
@ -1413,7 +1410,7 @@ async function transitionKycSatisfied(
async function processWithdrawalGroupPendingKyc(
ws: InternalWalletState,
withdrawalGroup: WithdrawalGroupRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const userType = "individual";
const kycInfo = withdrawalGroup.kycPending;
if (!kycInfo) {
@ -1456,13 +1453,13 @@ async function processWithdrawalGroupPendingKyc(
);
}
});
return OperationAttemptResult.longpoll();
return TaskRunResult.longpoll();
}
async function processWithdrawalGroupPendingReady(
ws: InternalWalletState,
withdrawalGroup: WithdrawalGroupRecord,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
const { withdrawalGroupId } = withdrawalGroup;
const transactionId = constructTransactionIdentifier({
tag: TransactionType.Withdrawal,
@ -1494,7 +1491,7 @@ async function processWithdrawalGroupPendingReady(
};
});
notifyTransition(ws, transactionId, transitionInfo);
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
}
const numTotalCoins = withdrawalGroup.denomsSel.selectedDenoms
@ -1608,7 +1605,7 @@ async function processWithdrawalGroupPendingReady(
if (numPlanchetErrors > 0) {
return {
type: OperationAttemptResultType.Error,
type: TaskRunResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_WITHDRAWAL_GROUP_INCOMPLETE,
{
@ -1619,16 +1616,13 @@ async function processWithdrawalGroupPendingReady(
};
}
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
}
export async function processWithdrawalGroup(
ws: InternalWalletState,
withdrawalGroupId: string,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
logger.trace("processing withdrawal group", withdrawalGroupId);
const withdrawalGroup = await ws.db
.mktx((x) => [x.withdrawalGroups])
@ -1646,7 +1640,7 @@ export async function processWithdrawalGroup(
if (ws.activeLongpoll[retryTag]) {
logger.info("withdrawal group already in long-polling, returning!");
return {
type: OperationAttemptResultType.Longpoll,
type: TaskRunResultType.Longpoll,
};
}
@ -1663,7 +1657,7 @@ export async function processWithdrawalGroup(
"returning early from withdrawal for long-polling in background",
);
return {
type: OperationAttemptResultType.Longpoll,
type: TaskRunResultType.Longpoll,
};
}
case WithdrawalGroupStatus.PendingWaitConfirmBank: {
@ -1671,15 +1665,9 @@ export async function processWithdrawalGroup(
switch (res.status) {
case BankStatusResultCode.Aborted:
case BankStatusResultCode.Done:
return {
type: OperationAttemptResultType.Finished,
result: undefined,
};
return TaskRunResult.finished();
case BankStatusResultCode.Waiting: {
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
return TaskRunResult.pending();
}
}
break;
@ -1687,14 +1675,11 @@ export async function processWithdrawalGroup(
case WithdrawalGroupStatus.Finished:
case WithdrawalGroupStatus.FailedBankAborted: {
// FIXME
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
return TaskRunResult.pending();
}
case WithdrawalGroupStatus.PendingAml:
// FIXME: Handle this case, withdrawal doesn't support AML yet.
return OperationAttemptResult.pendingEmpty();
return TaskRunResult.pending();
case WithdrawalGroupStatus.PendingKyc:
return processWithdrawalGroupPendingKyc(ws, withdrawalGroup);
case WithdrawalGroupStatus.PendingReady:
@ -1713,7 +1698,7 @@ export async function processWithdrawalGroup(
case WithdrawalGroupStatus.SuspendedRegisteringBank:
case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
// Nothing to do.
return OperationAttemptResult.finishedEmpty();
return TaskRunResult.finished();
default:
assertUnreachable(withdrawalGroup.status);
}

View File

@ -171,6 +171,7 @@ import {
import { setWalletDeviceId } from "./operations/backup/state.js";
import { getBalanceDetail, getBalances } from "./operations/balance.js";
import {
TaskRunResult,
getExchangeTosStatus,
makeExchangeListItem,
runTaskWithErrorReporting,
@ -287,7 +288,6 @@ import {
GetReadWriteAccess,
} from "./util/query.js";
import {
OperationAttemptResult,
TaskIdentifiers,
} from "./operations/common.js";
import { TimerAPI, TimerGroup } from "./util/timer.js";
@ -320,7 +320,7 @@ const logger = new Logger("wallet.ts");
async function callOperationHandler(
ws: InternalWalletState,
pending: PendingTaskInfo,
): Promise<OperationAttemptResult> {
): Promise<TaskRunResult> {
switch (pending.type) {
case PendingTaskType.ExchangeUpdate:
return await updateExchangeFromUrlHandler(ws, pending.exchangeBaseUrl);

View File

@ -54,6 +54,11 @@ setPRNG(function (x: Uint8Array, n: number) {
const logger = new Logger("taler-wallet-embedded/index.ts");
/**
* Sends JSON to the host application, i.e. the process that
* runs the JavaScript interpreter (quickjs / qtart) to run
* the embedded wallet.
*/
function sendNativeMessage(ev: CoreApiMessageEnvelope): void {
const m = JSON.stringify(ev);
qjsOs.postMessageToHost(m);
@ -183,21 +188,25 @@ export function installNativeWalletListener(): void {
const id = msg.id;
logger.info(`native listener: got request for ${operation} (${id})`);
let respMsg: CoreApiResponse;
try {
respMsg = await handler.handleMessage(operation, id, msg.args ?? {});
} catch (e) {
respMsg = {
type: "error",
id,
operation,
error: getErrorDetailFromException(e),
};
if (operation === "anastasisReduce") {
sendNativeMessage(respMsg);
} else {
let respMsg: CoreApiResponse;
try {
respMsg = await handler.handleMessage(operation, id, msg.args ?? {});
} catch (e) {
respMsg = {
type: "error",
id,
operation,
error: getErrorDetailFromException(e),
};
}
logger.info(
`native listener: sending back ${respMsg.type} message for operation ${operation} (${id})`,
);
sendNativeMessage(respMsg);
}
logger.info(
`native listener: sending back ${respMsg.type} message for operation ${operation} (${id})`,
);
sendNativeMessage(respMsg);
};
qjsOs.setMessageFromHostHandler((m) => onMessage(m));