DD37 wip
This commit is contained in:
parent
1b0bec0363
commit
60805f3ff8
@ -89,7 +89,6 @@ export enum TransactionMajorState {
|
||||
Aborting = "aborting",
|
||||
Aborted = "aborted",
|
||||
Suspended = "suspended",
|
||||
SuspendedDeletable = "suspended-deletable",
|
||||
Dialog = "dialog",
|
||||
SuspendedAborting = "suspended-aborting",
|
||||
Failed = "failed",
|
||||
@ -105,6 +104,7 @@ export enum TransactionMinorState {
|
||||
Deposit = "deposit",
|
||||
KycRequired = "kyc",
|
||||
AmlRequired = "aml",
|
||||
MergeKycRequired = "merge-kyc",
|
||||
Track = "track",
|
||||
Pay = "pay",
|
||||
RebindSession = "rebind-session",
|
||||
@ -113,8 +113,13 @@ export enum TransactionMinorState {
|
||||
AutoRefund = "auto-refund",
|
||||
User = "user",
|
||||
Bank = "bank",
|
||||
Exchange = "exchange",
|
||||
ClaimProposal = "claim-proposal",
|
||||
CheckRefunds = "check-refunds",
|
||||
CreatePurse = "create-purse",
|
||||
DeletePurse = "delete-purse",
|
||||
Ready = "ready",
|
||||
Merge = "merge",
|
||||
Repurchase = "repurchase",
|
||||
BankRegisterReserve = "bank-register-reserve",
|
||||
BankConfirmTransfer = "bank-confirm-transfer",
|
||||
@ -122,6 +127,9 @@ export enum TransactionMinorState {
|
||||
ExchangeWaitReserve = "exchange-wait-reserve",
|
||||
AbortingBank = "aborting-bank",
|
||||
Refused = "refused",
|
||||
Withdraw = "withdraw",
|
||||
MerchantOrderProposed = "merchant-order-proposed",
|
||||
Proposed = "proposed",
|
||||
}
|
||||
|
||||
export interface TransactionsResponse {
|
||||
|
@ -444,7 +444,7 @@ transactionsCli
|
||||
});
|
||||
|
||||
transactionsCli
|
||||
.subcommand("cancelAbortingTransaction", "suspend", {
|
||||
.subcommand("cancelAbortingTransaction", "cancel-aborting", {
|
||||
help: "Cancel the attempt of properly aborting a transaction.",
|
||||
})
|
||||
.requiredArgument("transactionId", clk.STRING, {
|
||||
|
@ -130,10 +130,10 @@ export enum OperationStatusRange {
|
||||
// Operations that need to be actively processed.
|
||||
ACTIVE_START = 10,
|
||||
ACTIVE_END = 29,
|
||||
// Operations that need user input, but nothing can be done
|
||||
// automatically.
|
||||
USER_ATTENTION_START = 30,
|
||||
USER_ATTENTION_END = 49,
|
||||
// Operations that are suspended and might
|
||||
// expire, but nothing else can be done.
|
||||
SUSPENDED_START = 30,
|
||||
SUSPENDED_END = 49,
|
||||
// Operations that don't need any attention or processing.
|
||||
DORMANT_START = 50,
|
||||
DORMANT_END = 69,
|
||||
@ -146,24 +146,24 @@ export enum WithdrawalGroupStatus {
|
||||
/**
|
||||
* Reserve must be registered with the bank.
|
||||
*/
|
||||
RegisteringBank = 10,
|
||||
PendingRegisteringBank = 10,
|
||||
|
||||
/**
|
||||
* We've registered reserve's information with the bank
|
||||
* and are now waiting for the user to confirm the withdraw
|
||||
* with the bank (typically 2nd factor auth).
|
||||
*/
|
||||
WaitConfirmBank = 11,
|
||||
PendingWaitConfirmBank = 11,
|
||||
|
||||
/**
|
||||
* Querying reserve status with the exchange.
|
||||
*/
|
||||
QueryingStatus = 12,
|
||||
PendingQueryingStatus = 12,
|
||||
|
||||
/**
|
||||
* Ready for withdrawal.
|
||||
*/
|
||||
Ready = 13,
|
||||
PendingReady = 13,
|
||||
|
||||
/**
|
||||
* We are telling the bank that we don't want to complete
|
||||
@ -174,12 +174,20 @@ export enum WithdrawalGroupStatus {
|
||||
/**
|
||||
* Exchange wants KYC info from the user.
|
||||
*/
|
||||
Kyc = 16,
|
||||
PendingKyc = 16,
|
||||
|
||||
/**
|
||||
* Exchange is doing AML checks.
|
||||
*/
|
||||
Aml = 17,
|
||||
PendingAml = 17,
|
||||
|
||||
SuspendedRegisteringBank = 30,
|
||||
SuspendedWaitConfirmBank = 31,
|
||||
SuspendedQueryingStatus = 32,
|
||||
SuspendedReady = 33,
|
||||
SuspendedAbortingBank = 34,
|
||||
SuspendedKyc = 35,
|
||||
SuspendedAml = 36,
|
||||
|
||||
/**
|
||||
* The corresponding withdraw record has been created.
|
||||
@ -191,16 +199,16 @@ export enum WithdrawalGroupStatus {
|
||||
/**
|
||||
* The bank aborted the withdrawal.
|
||||
*/
|
||||
BankAborted = 51,
|
||||
FailedBankAborted = 51,
|
||||
|
||||
SuspendedRegisteringBank = 52,
|
||||
SuspendedWaitConfirmBank = 53,
|
||||
SuspendedQueryingStatus = 54,
|
||||
SuspendedReady = 55,
|
||||
SuspendedAbortingBank = 56,
|
||||
SuspendedKyc = 57,
|
||||
SuspendedAml = 58,
|
||||
FailedAbortingBank = 59,
|
||||
|
||||
/**
|
||||
* Aborted in a state where we were supposed to
|
||||
* talk to the exchange. Money might have been
|
||||
* wired or not.
|
||||
*/
|
||||
AbortedExchange = 60
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1790,8 +1798,18 @@ export enum PeerPushPaymentInitiationStatus {
|
||||
/**
|
||||
* Initiated, but no purse created yet.
|
||||
*/
|
||||
Initiated = 10 /* ACTIVE_START */,
|
||||
PurseCreated = 50 /* DORMANT_START */,
|
||||
PendingCreatePurse = 10 /* ACTIVE_START */,
|
||||
PendingReady = 11,
|
||||
AbortingDeletePurse = 12,
|
||||
AbortingRefresh = 13,
|
||||
|
||||
SuspendedCreatePurse = 30,
|
||||
SuspendedReady = 31,
|
||||
SuspendedAbortingDeletePurse = 32,
|
||||
SuspendedAbortingRefresh = 33,
|
||||
|
||||
Done = 50 /* DORMANT_START */,
|
||||
Aborted = 51,
|
||||
}
|
||||
|
||||
export interface PeerPushPaymentCoinSelection {
|
||||
@ -1856,14 +1874,18 @@ export interface PeerPushPaymentInitiationRecord {
|
||||
}
|
||||
|
||||
export enum PeerPullPaymentInitiationStatus {
|
||||
Initial = 10 /* ACTIVE_START */,
|
||||
PendingCreatePurse = 10 /* ACTIVE_START */,
|
||||
/**
|
||||
* Purse created, waiting for the other party to accept the
|
||||
* invoice and deposit money into it.
|
||||
*/
|
||||
PurseCreated = 11 /* ACTIVE_START + 1 */,
|
||||
KycRequired = 12 /* ACTIVE_START + 2 */,
|
||||
PurseDeposited = 50 /* DORMANT_START */,
|
||||
PendingReady = 11 /* ACTIVE_START + 1 */,
|
||||
PendingMergeKycRequired = 12 /* ACTIVE_START + 2 */,
|
||||
PendingWithdrawing = 13,
|
||||
SuspendedCreatePurse = 30,
|
||||
SuspendedReady = 31,
|
||||
SuspendedWithdrawing = 32,
|
||||
DonePurseDeposited = 50 /* DORMANT_START */,
|
||||
}
|
||||
|
||||
export interface PeerPullPaymentInitiationRecord {
|
||||
@ -1921,12 +1943,13 @@ export interface PeerPullPaymentInitiationRecord {
|
||||
export enum PeerPushPaymentIncomingStatus {
|
||||
Proposed = 30 /* USER_ATTENTION_START */,
|
||||
Accepted = 10 /* ACTIVE_START */,
|
||||
KycRequired = 11 /* ACTIVE_START + 1 */,
|
||||
MergeKycRequired = 11 /* ACTIVE_START + 1 */,
|
||||
/**
|
||||
* Merge was successful and withdrawal group has been created, now
|
||||
* everything is in the hand of the withdrawal group.
|
||||
*/
|
||||
WithdrawalCreated = 50 /* DORMANT_START */,
|
||||
Withdrawing = 12,
|
||||
Done = 50 /* DORMANT_START */,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,7 +177,7 @@ export interface InternalWalletState {
|
||||
*
|
||||
* Used to allow processing of new work faster.
|
||||
*/
|
||||
latch: AsyncCondition;
|
||||
workAvailable: AsyncCondition;
|
||||
|
||||
listeners: NotificationListener[];
|
||||
|
||||
|
@ -553,7 +553,7 @@ export async function importBackup(
|
||||
reservePub,
|
||||
status: backupWg.timestamp_finish
|
||||
? WithdrawalGroupStatus.Finished
|
||||
: WithdrawalGroupStatus.QueryingStatus, // FIXME!
|
||||
: WithdrawalGroupStatus.PendingQueryingStatus, // FIXME!
|
||||
timestampStart: backupWg.timestamp_created,
|
||||
wgInfo,
|
||||
restrictAge: backupWg.restrict_age,
|
||||
|
@ -492,7 +492,7 @@ export function runLongpollAsync(
|
||||
if (!res.ready) {
|
||||
await storeOperationPending(ws, retryTag);
|
||||
}
|
||||
ws.latch.trigger();
|
||||
ws.workAvailable.trigger();
|
||||
};
|
||||
asyncFn();
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ export async function resumeDepositGroup(
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
ws.latch.trigger();
|
||||
ws.workAvailable.trigger();
|
||||
if (res) {
|
||||
ws.notify({
|
||||
type: NotificationType.TransactionStateTransition,
|
||||
@ -301,7 +301,7 @@ export async function abortDepositGroup(
|
||||
});
|
||||
stopLongpolling(ws, retryTag);
|
||||
// Need to process the operation again.
|
||||
ws.latch.trigger();
|
||||
ws.workAvailable.trigger();
|
||||
if (res) {
|
||||
ws.notify({
|
||||
type: NotificationType.TransactionStateTransition,
|
||||
|
@ -2410,7 +2410,7 @@ export async function processPurchaseQueryRefund(
|
||||
return OperationAttemptResult.finishedEmpty();
|
||||
}
|
||||
|
||||
export async function abortPay(
|
||||
export async function abortPayMerchant(
|
||||
ws: InternalWalletState,
|
||||
proposalId: string,
|
||||
cancelImmediately?: boolean,
|
||||
@ -2499,6 +2499,7 @@ export function computePayMerchantTransactionState(
|
||||
case PurchaseStatus.Proposed:
|
||||
return {
|
||||
major: TransactionMajorState.Dialog,
|
||||
minor: TransactionMinorState.MerchantOrderProposed,
|
||||
};
|
||||
case PurchaseStatus.ProposalDownloadFailed:
|
||||
return {
|
||||
|
@ -75,20 +75,21 @@ import {
|
||||
NotificationType,
|
||||
HttpStatusCode,
|
||||
codecForWalletKycUuid,
|
||||
WalletKycUuid,
|
||||
TransactionState,
|
||||
TransactionMajorState,
|
||||
TransactionMinorState,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { SpendCoinDetails } from "../crypto/cryptoImplementation.js";
|
||||
import {
|
||||
DenominationRecord,
|
||||
KycPendingInfo,
|
||||
KycUserType,
|
||||
OperationStatus,
|
||||
PeerPullPaymentIncomingRecord,
|
||||
PeerPullPaymentIncomingStatus,
|
||||
PeerPullPaymentInitiationRecord,
|
||||
PeerPullPaymentInitiationStatus,
|
||||
PeerPushPaymentCoinSelection,
|
||||
PeerPushPaymentIncomingRecord,
|
||||
PeerPushPaymentIncomingStatus,
|
||||
PeerPushPaymentInitiationRecord,
|
||||
PeerPushPaymentInitiationStatus,
|
||||
ReserveRecord,
|
||||
WithdrawalGroupStatus,
|
||||
@ -98,7 +99,6 @@ import { TalerError } from "@gnu-taler/taler-util";
|
||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||
import {
|
||||
LongpollResult,
|
||||
makeTransactionId,
|
||||
resetOperationTimeout,
|
||||
runLongpollAsync,
|
||||
runOperationWithErrorReporting,
|
||||
@ -128,8 +128,10 @@ import {
|
||||
import { PendingTaskType } from "../pending-types.js";
|
||||
import {
|
||||
constructTransactionIdentifier,
|
||||
notifyTransition,
|
||||
stopLongpolling,
|
||||
} from "./transactions.js";
|
||||
import { assertUnreachable } from "../util/assertUnreachable.js";
|
||||
|
||||
const logger = new Logger("operations/peer-to-peer.ts");
|
||||
|
||||
@ -451,19 +453,11 @@ export async function checkPeerPushDebit(
|
||||
};
|
||||
}
|
||||
|
||||
export async function processPeerPushInitiation(
|
||||
async function processPeerPushDebitCreateReserve(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
peerPushInitiation: PeerPushPaymentInitiationRecord,
|
||||
): Promise<OperationAttemptResult> {
|
||||
const peerPushInitiation = await ws.db
|
||||
.mktx((x) => [x.peerPushPaymentInitiations])
|
||||
.runReadOnly(async (tx) => {
|
||||
return tx.peerPushPaymentInitiations.get(pursePub);
|
||||
});
|
||||
if (!peerPushInitiation) {
|
||||
throw Error("peer push payment not found");
|
||||
}
|
||||
|
||||
const pursePub = peerPushInitiation.pursePub;
|
||||
const purseExpiration = peerPushInitiation.purseExpiration;
|
||||
const hContractTerms = peerPushInitiation.contractTermsHash;
|
||||
|
||||
@ -501,22 +495,25 @@ export async function processPeerPushInitiation(
|
||||
peerPushInitiation.exchangeBaseUrl,
|
||||
);
|
||||
|
||||
const httpResp = await ws.http.postJson(createPurseUrl.href, {
|
||||
amount: peerPushInitiation.amount,
|
||||
merge_pub: peerPushInitiation.mergePub,
|
||||
purse_sig: purseSigResp.sig,
|
||||
h_contract_terms: hContractTerms,
|
||||
purse_expiration: purseExpiration,
|
||||
deposits: depositSigsResp.deposits,
|
||||
min_age: 0,
|
||||
econtract: econtractResp.econtract,
|
||||
const httpResp = await ws.http.fetch(createPurseUrl.href, {
|
||||
method: "POST",
|
||||
body: {
|
||||
amount: peerPushInitiation.amount,
|
||||
merge_pub: peerPushInitiation.mergePub,
|
||||
purse_sig: purseSigResp.sig,
|
||||
h_contract_terms: hContractTerms,
|
||||
purse_expiration: purseExpiration,
|
||||
deposits: depositSigsResp.deposits,
|
||||
min_age: 0,
|
||||
econtract: econtractResp.econtract,
|
||||
},
|
||||
});
|
||||
|
||||
const resp = await httpResp.json();
|
||||
|
||||
logger.info(`resp: ${j2s(resp)}`);
|
||||
|
||||
if (httpResp.status !== 200) {
|
||||
if (httpResp.status !== HttpStatusCode.Ok) {
|
||||
throw Error("got error response from exchange");
|
||||
}
|
||||
|
||||
@ -527,7 +524,7 @@ export async function processPeerPushInitiation(
|
||||
if (!ppi) {
|
||||
return;
|
||||
}
|
||||
ppi.status = PeerPushPaymentInitiationStatus.PurseCreated;
|
||||
ppi.status = PeerPushPaymentInitiationStatus.Done;
|
||||
await tx.peerPushPaymentInitiations.put(ppi);
|
||||
});
|
||||
|
||||
@ -537,6 +534,122 @@ export async function processPeerPushInitiation(
|
||||
};
|
||||
}
|
||||
|
||||
async function transitionPeerPushDebitFromReadyToDone(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
): Promise<void> {
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPushPaymentInitiations])
|
||||
.runReadWrite(async (tx) => {
|
||||
const ppiRec = await tx.peerPushPaymentInitiations.get(pursePub);
|
||||
if (!ppiRec) {
|
||||
return undefined;
|
||||
}
|
||||
if (ppiRec.status !== PeerPushPaymentInitiationStatus.PendingReady) {
|
||||
return undefined;
|
||||
}
|
||||
const oldTxState = computePeerPushDebitTransactionState(ppiRec);
|
||||
ppiRec.status = PeerPushPaymentInitiationStatus.Done;
|
||||
const newTxState = computePeerPushDebitTransactionState(ppiRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the "pending(ready)" state of a peer-push-debit transaction.
|
||||
*/
|
||||
async function processPeerPushDebitReady(
|
||||
ws: InternalWalletState,
|
||||
peerPushInitiation: PeerPushPaymentInitiationRecord,
|
||||
): Promise<OperationAttemptResult> {
|
||||
const pursePub = peerPushInitiation.pursePub;
|
||||
const retryTag = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
runLongpollAsync(ws, retryTag, async (ct) => {
|
||||
const mergeUrl = new URL(`purses/${pursePub}/merge`);
|
||||
mergeUrl.searchParams.set("timeout_ms", "30000");
|
||||
const resp = await ws.http.fetch(mergeUrl.href, {
|
||||
// timeout: getReserveRequestTimeout(withdrawalGroup),
|
||||
cancellationToken: ct,
|
||||
});
|
||||
if (resp.status === HttpStatusCode.Ok) {
|
||||
const purseStatus = await readSuccessResponseJsonOrThrow(
|
||||
resp,
|
||||
codecForExchangePurseStatus(),
|
||||
);
|
||||
if (purseStatus.deposit_timestamp) {
|
||||
await transitionPeerPushDebitFromReadyToDone(
|
||||
ws,
|
||||
peerPushInitiation.pursePub,
|
||||
);
|
||||
return {
|
||||
ready: true,
|
||||
};
|
||||
}
|
||||
} else if (resp.status === HttpStatusCode.Gone) {
|
||||
// FIXME: transition the reserve into the expired state
|
||||
}
|
||||
return {
|
||||
ready: false,
|
||||
};
|
||||
});
|
||||
logger.trace(
|
||||
"returning early from withdrawal for long-polling in background",
|
||||
);
|
||||
return {
|
||||
type: OperationAttemptResultType.Longpoll,
|
||||
};
|
||||
}
|
||||
|
||||
export async function processPeerPushDebit(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
): Promise<OperationAttemptResult> {
|
||||
const peerPushInitiation = await ws.db
|
||||
.mktx((x) => [x.peerPushPaymentInitiations])
|
||||
.runReadOnly(async (tx) => {
|
||||
return tx.peerPushPaymentInitiations.get(pursePub);
|
||||
});
|
||||
if (!peerPushInitiation) {
|
||||
throw Error("peer push payment not found");
|
||||
}
|
||||
|
||||
const retryTag = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
|
||||
// We're already running!
|
||||
if (ws.activeLongpoll[retryTag]) {
|
||||
logger.info("peer-push-debit task already in long-polling, returning!");
|
||||
return {
|
||||
type: OperationAttemptResultType.Longpoll,
|
||||
};
|
||||
}
|
||||
|
||||
switch (peerPushInitiation.status) {
|
||||
case PeerPushPaymentInitiationStatus.PendingCreatePurse:
|
||||
return processPeerPushDebitCreateReserve(ws, peerPushInitiation);
|
||||
case PeerPushPaymentInitiationStatus.PendingReady:
|
||||
return processPeerPushDebitReady(ws, peerPushInitiation);
|
||||
}
|
||||
|
||||
return {
|
||||
type: OperationAttemptResultType.Finished,
|
||||
result: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate sending a peer-to-peer push payment.
|
||||
*/
|
||||
@ -612,7 +725,7 @@ export async function initiatePeerPushPayment(
|
||||
pursePriv: pursePair.priv,
|
||||
pursePub: pursePair.pub,
|
||||
timestampCreated: TalerProtocolTimestamp.now(),
|
||||
status: PeerPushPaymentInitiationStatus.Initiated,
|
||||
status: PeerPushPaymentInitiationStatus.PendingCreatePurse,
|
||||
contractTerms: contractTerms,
|
||||
coinSel: {
|
||||
coinPubs: sel.coins.map((x) => x.coinPub),
|
||||
@ -628,12 +741,12 @@ export async function initiatePeerPushPayment(
|
||||
});
|
||||
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushInitiation,
|
||||
tag: PendingTaskType.PeerPushDebit,
|
||||
pursePub: pursePair.pub,
|
||||
});
|
||||
|
||||
await runOperationWithErrorReporting(ws, taskId, async () => {
|
||||
return await processPeerPushInitiation(ws, pursePair.pub);
|
||||
return await processPeerPushDebit(ws, pursePair.pub);
|
||||
});
|
||||
|
||||
return {
|
||||
@ -645,22 +758,24 @@ export async function initiatePeerPushPayment(
|
||||
exchangeBaseUrl: coinSelRes.result.exchangeBaseUrl,
|
||||
contractPriv: contractKeyPair.priv,
|
||||
}),
|
||||
transactionId: makeTransactionId(
|
||||
TransactionType.PeerPushDebit,
|
||||
pursePair.pub,
|
||||
),
|
||||
transactionId: constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushDebit,
|
||||
pursePub: pursePair.pub,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
interface ExchangePurseStatus {
|
||||
balance: AmountString;
|
||||
deposit_timestamp?: TalerProtocolTimestamp;
|
||||
merge_timestamp?: TalerProtocolTimestamp;
|
||||
}
|
||||
|
||||
export const codecForExchangePurseStatus = (): Codec<ExchangePurseStatus> =>
|
||||
buildCodecForObject<ExchangePurseStatus>()
|
||||
.property("balance", codecForAmountString())
|
||||
.property("deposit_timestamp", codecOptional(codecForTimestamp))
|
||||
.property("merge_timestamp", codecOptional(codecForTimestamp))
|
||||
.build("ExchangePurseStatus");
|
||||
|
||||
export async function preparePeerPushCredit(
|
||||
@ -879,13 +994,13 @@ export async function processPeerPushCredit(
|
||||
const amount = Amounts.parseOrThrow(contractTerms.amount);
|
||||
|
||||
if (
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.KycRequired &&
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.MergeKycRequired &&
|
||||
peerInc.kycInfo
|
||||
) {
|
||||
const txId = makeTransactionId(
|
||||
TransactionType.PeerPushCredit,
|
||||
peerInc.peerPushPaymentIncomingId,
|
||||
);
|
||||
const txId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushCredit,
|
||||
peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId,
|
||||
});
|
||||
await checkWithdrawalKycStatus(
|
||||
ws,
|
||||
peerInc.exchangeBaseUrl,
|
||||
@ -951,7 +1066,7 @@ export async function processPeerPushCredit(
|
||||
paytoHash: kycPending.h_payto,
|
||||
requirementRow: kycPending.requirement_row,
|
||||
};
|
||||
peerInc.status = PeerPushPaymentIncomingStatus.KycRequired;
|
||||
peerInc.status = PeerPushPaymentIncomingStatus.MergeKycRequired;
|
||||
await tx.peerPushPaymentIncoming.put(peerInc);
|
||||
});
|
||||
return {
|
||||
@ -975,7 +1090,7 @@ export async function processPeerPushCredit(
|
||||
},
|
||||
forcedWithdrawalGroupId: peerInc.withdrawalGroupId,
|
||||
exchangeBaseUrl: peerInc.exchangeBaseUrl,
|
||||
reserveStatus: WithdrawalGroupStatus.QueryingStatus,
|
||||
reserveStatus: WithdrawalGroupStatus.PendingQueryingStatus,
|
||||
reserveKeyPair: {
|
||||
priv: mergeReserveInfo.reservePriv,
|
||||
pub: mergeReserveInfo.reservePub,
|
||||
@ -993,9 +1108,9 @@ export async function processPeerPushCredit(
|
||||
}
|
||||
if (
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.Accepted ||
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.KycRequired
|
||||
peerInc.status === PeerPushPaymentIncomingStatus.MergeKycRequired
|
||||
) {
|
||||
peerInc.status = PeerPushPaymentIncomingStatus.WithdrawalCreated;
|
||||
peerInc.status = PeerPushPaymentIncomingStatus.Done;
|
||||
}
|
||||
await tx.peerPushPaymentIncoming.put(peerInc);
|
||||
});
|
||||
@ -1011,7 +1126,7 @@ export async function confirmPeerPushCredit(
|
||||
req: ConfirmPeerPushCreditRequest,
|
||||
): Promise<AcceptPeerPushPaymentResponse> {
|
||||
let peerInc: PeerPushPaymentIncomingRecord | undefined;
|
||||
let contractTerms: PeerContractTerms | undefined;
|
||||
|
||||
await ws.db
|
||||
.mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming])
|
||||
.runReadWrite(async (tx) => {
|
||||
@ -1021,10 +1136,6 @@ export async function confirmPeerPushCredit(
|
||||
if (!peerInc) {
|
||||
return;
|
||||
}
|
||||
const ctRec = await tx.contractTerms.get(peerInc.contractTermsHash);
|
||||
if (ctRec) {
|
||||
contractTerms = ctRec.contractTermsRaw;
|
||||
}
|
||||
if (peerInc.status === PeerPushPaymentIncomingStatus.Proposed) {
|
||||
peerInc.status = PeerPushPaymentIncomingStatus.Accepted;
|
||||
}
|
||||
@ -1037,21 +1148,15 @@ export async function confirmPeerPushCredit(
|
||||
);
|
||||
}
|
||||
|
||||
checkDbInvariant(!!contractTerms);
|
||||
ws.workAvailable.trigger();
|
||||
|
||||
await updateExchangeFromUrl(ws, peerInc.exchangeBaseUrl);
|
||||
|
||||
const retryTag = TaskIdentifiers.forPeerPushCredit(peerInc);
|
||||
|
||||
await runOperationWithErrorReporting(ws, retryTag, () =>
|
||||
processPeerPushCredit(ws, req.peerPushPaymentIncomingId),
|
||||
);
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushCredit,
|
||||
peerPushPaymentIncomingId: req.peerPushPaymentIncomingId,
|
||||
});
|
||||
|
||||
return {
|
||||
transactionId: makeTransactionId(
|
||||
TransactionType.PeerPushCredit,
|
||||
req.peerPushPaymentIncomingId,
|
||||
),
|
||||
transactionId,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1209,11 +1314,13 @@ export async function confirmPeerPullDebit(
|
||||
},
|
||||
);
|
||||
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPullDebit,
|
||||
peerPullPaymentIncomingId: req.peerPullPaymentIncomingId,
|
||||
});
|
||||
|
||||
return {
|
||||
transactionId: makeTransactionId(
|
||||
TransactionType.PeerPullDebit,
|
||||
req.peerPullPaymentIncomingId,
|
||||
),
|
||||
transactionId,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1395,7 +1502,7 @@ export async function queryPurseForPeerPullCredit(
|
||||
},
|
||||
forcedWithdrawalGroupId: pullIni.withdrawalGroupId,
|
||||
exchangeBaseUrl: pullIni.exchangeBaseUrl,
|
||||
reserveStatus: WithdrawalGroupStatus.QueryingStatus,
|
||||
reserveStatus: WithdrawalGroupStatus.PendingQueryingStatus,
|
||||
reserveKeyPair: {
|
||||
priv: reserve.reservePriv,
|
||||
pub: reserve.reservePub,
|
||||
@ -1410,8 +1517,8 @@ export async function queryPurseForPeerPullCredit(
|
||||
logger.warn("peerPullPaymentInitiation not found anymore");
|
||||
return;
|
||||
}
|
||||
if (finPi.status === PeerPullPaymentInitiationStatus.PurseCreated) {
|
||||
finPi.status = PeerPullPaymentInitiationStatus.PurseDeposited;
|
||||
if (finPi.status === PeerPullPaymentInitiationStatus.PendingReady) {
|
||||
finPi.status = PeerPullPaymentInitiationStatus.DonePurseDeposited;
|
||||
}
|
||||
await tx.peerPullPaymentInitiations.put(finPi);
|
||||
});
|
||||
@ -1434,7 +1541,7 @@ export async function processPeerPullCredit(
|
||||
}
|
||||
|
||||
const retryTag = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPullInitiation,
|
||||
tag: PendingTaskType.PeerPullCredit,
|
||||
pursePub,
|
||||
});
|
||||
|
||||
@ -1449,7 +1556,7 @@ export async function processPeerPullCredit(
|
||||
logger.trace(`processing ${retryTag}, status=${pullIni.status}`);
|
||||
|
||||
switch (pullIni.status) {
|
||||
case PeerPullPaymentInitiationStatus.PurseDeposited: {
|
||||
case PeerPullPaymentInitiationStatus.DonePurseDeposited: {
|
||||
// We implement this case so that the "retry" action on a peer-pull-credit transaction
|
||||
// also retries the withdrawal task.
|
||||
|
||||
@ -1475,7 +1582,7 @@ export async function processPeerPullCredit(
|
||||
result: undefined,
|
||||
};
|
||||
}
|
||||
case PeerPullPaymentInitiationStatus.PurseCreated:
|
||||
case PeerPullPaymentInitiationStatus.PendingReady:
|
||||
runLongpollAsync(ws, retryTag, async (cancellationToken) =>
|
||||
queryPurseForPeerPullCredit(ws, pullIni, cancellationToken),
|
||||
);
|
||||
@ -1485,23 +1592,23 @@ export async function processPeerPullCredit(
|
||||
return {
|
||||
type: OperationAttemptResultType.Longpoll,
|
||||
};
|
||||
case PeerPullPaymentInitiationStatus.KycRequired: {
|
||||
case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: {
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPullCredit,
|
||||
pursePub: pullIni.pursePub,
|
||||
});
|
||||
if (pullIni.kycInfo) {
|
||||
const txId = makeTransactionId(
|
||||
TransactionType.PeerPullCredit,
|
||||
pullIni.pursePub,
|
||||
);
|
||||
await checkWithdrawalKycStatus(
|
||||
ws,
|
||||
pullIni.exchangeBaseUrl,
|
||||
txId,
|
||||
transactionId,
|
||||
pullIni.kycInfo,
|
||||
"individual",
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PeerPullPaymentInitiationStatus.Initial:
|
||||
case PeerPullPaymentInitiationStatus.PendingCreatePurse:
|
||||
break;
|
||||
default:
|
||||
throw Error(`unknown PeerPullPaymentInitiationStatus ${pullIni.status}`);
|
||||
@ -1590,7 +1697,8 @@ export async function processPeerPullCredit(
|
||||
paytoHash: kycPending.h_payto,
|
||||
requirementRow: kycPending.requirement_row,
|
||||
};
|
||||
peerIni.status = PeerPullPaymentInitiationStatus.KycRequired;
|
||||
peerIni.status =
|
||||
PeerPullPaymentInitiationStatus.PendingMergeKycRequired;
|
||||
await tx.peerPullPaymentInitiations.put(peerIni);
|
||||
});
|
||||
return {
|
||||
@ -1610,7 +1718,7 @@ export async function processPeerPullCredit(
|
||||
if (!pi2) {
|
||||
return;
|
||||
}
|
||||
pi2.status = PeerPullPaymentInitiationStatus.PurseCreated;
|
||||
pi2.status = PeerPullPaymentInitiationStatus.PendingReady;
|
||||
await tx.peerPullPaymentInitiations.put(pi2);
|
||||
});
|
||||
|
||||
@ -1776,7 +1884,7 @@ export async function initiatePeerPullPayment(
|
||||
pursePub: pursePair.pub,
|
||||
mergePriv: mergePair.priv,
|
||||
mergePub: mergePair.pub,
|
||||
status: PeerPullPaymentInitiationStatus.Initial,
|
||||
status: PeerPullPaymentInitiationStatus.PendingCreatePurse,
|
||||
contractTerms: contractTerms,
|
||||
mergeTimestamp,
|
||||
mergeReserveRowId: mergeReserveRowId,
|
||||
@ -1796,7 +1904,7 @@ export async function initiatePeerPullPayment(
|
||||
// check this asynchronously from the transaction status?
|
||||
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPullInitiation,
|
||||
tag: PendingTaskType.PeerPullCredit,
|
||||
pursePub: pursePair.pub,
|
||||
});
|
||||
|
||||
@ -1804,14 +1912,408 @@ export async function initiatePeerPullPayment(
|
||||
return processPeerPullCredit(ws, pursePair.pub);
|
||||
});
|
||||
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPullCredit,
|
||||
pursePub: pursePair.pub,
|
||||
});
|
||||
|
||||
return {
|
||||
talerUri: constructPayPullUri({
|
||||
exchangeBaseUrl: exchangeBaseUrl,
|
||||
contractPriv: contractKeyPair.priv,
|
||||
}),
|
||||
transactionId: makeTransactionId(
|
||||
TransactionType.PeerPullCredit,
|
||||
pursePair.pub,
|
||||
),
|
||||
transactionId,
|
||||
};
|
||||
}
|
||||
|
||||
export function computePeerPushDebitTransactionState(
|
||||
ppiRecord: PeerPushPaymentInitiationRecord,
|
||||
): TransactionState {
|
||||
switch (ppiRecord.status) {
|
||||
case PeerPushPaymentInitiationStatus.PendingCreatePurse:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.CreatePurse,
|
||||
};
|
||||
case PeerPushPaymentInitiationStatus.PendingReady:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.Ready,
|
||||
};
|
||||
case PeerPushPaymentInitiationStatus.Aborted:
|
||||
return {
|
||||
major: TransactionMajorState.Aborted,
|
||||
};
|
||||
case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
|
||||
return {
|
||||
major: TransactionMajorState.Aborting,
|
||||
minor: TransactionMinorState.DeletePurse,
|
||||
};
|
||||
case PeerPushPaymentInitiationStatus.AbortingRefresh:
|
||||
return {
|
||||
major: TransactionMajorState.Aborting,
|
||||
minor: TransactionMinorState.Refresh,
|
||||
};
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
|
||||
return {
|
||||
major: TransactionMajorState.SuspendedAborting,
|
||||
minor: TransactionMinorState.DeletePurse,
|
||||
};
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
|
||||
return {
|
||||
major: TransactionMajorState.SuspendedAborting,
|
||||
minor: TransactionMinorState.Refresh,
|
||||
};
|
||||
case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
|
||||
return {
|
||||
major: TransactionMajorState.Suspended,
|
||||
minor: TransactionMinorState.CreatePurse,
|
||||
};
|
||||
case PeerPushPaymentInitiationStatus.SuspendedReady:
|
||||
return {
|
||||
major: TransactionMajorState.Suspended,
|
||||
minor: TransactionMinorState.Ready,
|
||||
};
|
||||
case PeerPushPaymentInitiationStatus.Done:
|
||||
return {
|
||||
major: TransactionMajorState.Done,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function abortPeerPushDebitTransaction(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPushPaymentInitiations])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
|
||||
if (!pushDebitRec) {
|
||||
logger.warn(`peer push debit ${pursePub} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
|
||||
switch (pushDebitRec.status) {
|
||||
case PeerPushPaymentInitiationStatus.PendingReady:
|
||||
case PeerPushPaymentInitiationStatus.SuspendedReady:
|
||||
newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
|
||||
case PeerPushPaymentInitiationStatus.PendingCreatePurse:
|
||||
// Network request might already be in-flight!
|
||||
newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
|
||||
case PeerPushPaymentInitiationStatus.AbortingRefresh:
|
||||
case PeerPushPaymentInitiationStatus.Done:
|
||||
case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
|
||||
case PeerPushPaymentInitiationStatus.Aborted:
|
||||
// Do nothing
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pushDebitRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
|
||||
pushDebitRec.status = newStatus;
|
||||
const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
|
||||
await tx.peerPushPaymentInitiations.put(pushDebitRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function cancelAbortingPeerPushDebitTransaction(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPushPaymentInitiations])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
|
||||
if (!pushDebitRec) {
|
||||
logger.warn(`peer push debit ${pursePub} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
|
||||
switch (pushDebitRec.status) {
|
||||
case PeerPushPaymentInitiationStatus.AbortingRefresh:
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
|
||||
// FIXME: We also need to abort the refresh group!
|
||||
newStatus = PeerPushPaymentInitiationStatus.Aborted;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
|
||||
newStatus = PeerPushPaymentInitiationStatus.Aborted;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.PendingReady:
|
||||
case PeerPushPaymentInitiationStatus.SuspendedReady:
|
||||
case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
|
||||
case PeerPushPaymentInitiationStatus.PendingCreatePurse:
|
||||
case PeerPushPaymentInitiationStatus.Done:
|
||||
case PeerPushPaymentInitiationStatus.Aborted:
|
||||
// Do nothing
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pushDebitRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
|
||||
pushDebitRec.status = newStatus;
|
||||
const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
|
||||
await tx.peerPushPaymentInitiations.put(pushDebitRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function suspendPeerPushDebitTransaction(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPushPaymentInitiations])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
|
||||
if (!pushDebitRec) {
|
||||
logger.warn(`peer push debit ${pursePub} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
|
||||
switch (pushDebitRec.status) {
|
||||
case PeerPushPaymentInitiationStatus.PendingCreatePurse:
|
||||
newStatus = PeerPushPaymentInitiationStatus.SuspendedCreatePurse;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.AbortingRefresh:
|
||||
newStatus = PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
|
||||
newStatus =
|
||||
PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.PendingReady:
|
||||
newStatus = PeerPushPaymentInitiationStatus.SuspendedReady;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
|
||||
case PeerPushPaymentInitiationStatus.SuspendedReady:
|
||||
case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
|
||||
case PeerPushPaymentInitiationStatus.Done:
|
||||
case PeerPushPaymentInitiationStatus.Aborted:
|
||||
// Do nothing
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pushDebitRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
|
||||
pushDebitRec.status = newStatus;
|
||||
const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
|
||||
await tx.peerPushPaymentInitiations.put(pushDebitRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function resumePeerPushDebitTransaction(
|
||||
ws: InternalWalletState,
|
||||
pursePub: string,
|
||||
) {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.PeerPushDebit,
|
||||
pursePub,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.peerPushPaymentInitiations])
|
||||
.runReadWrite(async (tx) => {
|
||||
const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
|
||||
if (!pushDebitRec) {
|
||||
logger.warn(`peer push debit ${pursePub} not found`);
|
||||
return;
|
||||
}
|
||||
let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
|
||||
switch (pushDebitRec.status) {
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
|
||||
newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
|
||||
newStatus = PeerPushPaymentInitiationStatus.AbortingRefresh;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.SuspendedReady:
|
||||
newStatus = PeerPushPaymentInitiationStatus.PendingReady;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
|
||||
newStatus = PeerPushPaymentInitiationStatus.PendingCreatePurse;
|
||||
break;
|
||||
case PeerPushPaymentInitiationStatus.PendingCreatePurse:
|
||||
case PeerPushPaymentInitiationStatus.AbortingRefresh:
|
||||
case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
|
||||
case PeerPushPaymentInitiationStatus.PendingReady:
|
||||
case PeerPushPaymentInitiationStatus.Done:
|
||||
case PeerPushPaymentInitiationStatus.Aborted:
|
||||
// Do nothing
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(pushDebitRec.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
|
||||
pushDebitRec.status = newStatus;
|
||||
const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
|
||||
await tx.peerPushPaymentInitiations.put(pushDebitRec);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export function computePeerPushCreditTransactionState(
|
||||
pushCreditRecord: PeerPushPaymentIncomingRecord,
|
||||
): TransactionState {
|
||||
switch (pushCreditRecord.status) {
|
||||
case PeerPushPaymentIncomingStatus.Proposed:
|
||||
return {
|
||||
major: TransactionMajorState.Dialog,
|
||||
minor: TransactionMinorState.Proposed,
|
||||
};
|
||||
case PeerPushPaymentIncomingStatus.Accepted:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.Merge,
|
||||
};
|
||||
case PeerPushPaymentIncomingStatus.Done:
|
||||
return {
|
||||
major: TransactionMajorState.Done,
|
||||
};
|
||||
case PeerPushPaymentIncomingStatus.MergeKycRequired:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.KycRequired,
|
||||
};
|
||||
case PeerPushPaymentIncomingStatus.Withdrawing:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.Withdraw,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function computePeerPullCreditTransactionState(
|
||||
pullCreditRecord: PeerPullPaymentInitiationRecord,
|
||||
): TransactionState {
|
||||
switch (pullCreditRecord.status) {
|
||||
case PeerPullPaymentInitiationStatus.PendingCreatePurse:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.CreatePurse,
|
||||
};
|
||||
case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.MergeKycRequired,
|
||||
};
|
||||
case PeerPullPaymentInitiationStatus.PendingReady:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.Ready,
|
||||
};
|
||||
case PeerPullPaymentInitiationStatus.DonePurseDeposited:
|
||||
return {
|
||||
major: TransactionMajorState.Done,
|
||||
};
|
||||
case PeerPullPaymentInitiationStatus.PendingWithdrawing:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.Withdraw,
|
||||
};
|
||||
case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
|
||||
return {
|
||||
major: TransactionMajorState.Suspended,
|
||||
minor: TransactionMinorState.CreatePurse,
|
||||
};
|
||||
case PeerPullPaymentInitiationStatus.SuspendedReady:
|
||||
return {
|
||||
major: TransactionMajorState.Suspended,
|
||||
minor: TransactionMinorState.Ready,
|
||||
};
|
||||
case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.Withdraw,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function computePeerPullDebitTransactionState(
|
||||
pullDebitRecord: PeerPullPaymentIncomingRecord,
|
||||
): TransactionState {
|
||||
switch (pullDebitRecord.status) {
|
||||
case PeerPullPaymentIncomingStatus.Proposed:
|
||||
return {
|
||||
major: TransactionMajorState.Dialog,
|
||||
minor: TransactionMinorState.Proposed,
|
||||
};
|
||||
case PeerPullPaymentIncomingStatus.Accepted:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.Deposit,
|
||||
};
|
||||
case PeerPullPaymentIncomingStatus.Paid:
|
||||
return {
|
||||
major: TransactionMajorState.Done,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -364,14 +364,14 @@ async function gatherPeerPullInitiationPending(
|
||||
resp: PendingOperationsResponse,
|
||||
): Promise<void> {
|
||||
await tx.peerPullPaymentInitiations.iter().forEachAsync(async (pi) => {
|
||||
if (pi.status === PeerPullPaymentInitiationStatus.PurseDeposited) {
|
||||
if (pi.status === PeerPullPaymentInitiationStatus.DonePurseDeposited) {
|
||||
return;
|
||||
}
|
||||
const opId = TaskIdentifiers.forPeerPullPaymentInitiation(pi);
|
||||
const retryRecord = await tx.operationRetries.get(opId);
|
||||
const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
|
||||
resp.pendingOperations.push({
|
||||
type: PendingTaskType.PeerPullInitiation,
|
||||
type: PendingTaskType.PeerPullCredit,
|
||||
...getPendingCommon(ws, opId, timestampDue),
|
||||
givesLifeness: true,
|
||||
retryInfo: retryRecord?.retryInfo,
|
||||
@ -421,14 +421,14 @@ async function gatherPeerPushInitiationPending(
|
||||
resp: PendingOperationsResponse,
|
||||
): Promise<void> {
|
||||
await tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => {
|
||||
if (pi.status === PeerPushPaymentInitiationStatus.PurseCreated) {
|
||||
if (pi.status === PeerPushPaymentInitiationStatus.Done) {
|
||||
return;
|
||||
}
|
||||
const opId = TaskIdentifiers.forPeerPushPaymentInitiation(pi);
|
||||
const retryRecord = await tx.operationRetries.get(opId);
|
||||
const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now();
|
||||
resp.pendingOperations.push({
|
||||
type: PendingTaskType.PeerPushInitiation,
|
||||
type: PendingTaskType.PeerPushDebit,
|
||||
...getPendingCommon(ws, opId, timestampDue),
|
||||
givesLifeness: true,
|
||||
retryInfo: retryRecord?.retryInfo,
|
||||
@ -450,7 +450,7 @@ async function gatherPeerPushCreditPending(
|
||||
switch (pi.status) {
|
||||
case PeerPushPaymentIncomingStatus.Proposed:
|
||||
return;
|
||||
case PeerPushPaymentIncomingStatus.WithdrawalCreated:
|
||||
case PeerPushPaymentIncomingStatus.Done:
|
||||
return;
|
||||
}
|
||||
const opId = TaskIdentifiers.forPeerPushCredit(pi);
|
||||
|
@ -400,7 +400,7 @@ export async function processRecoupGroupHandler(
|
||||
await internalCreateWithdrawalGroup(ws, {
|
||||
amount: Amounts.parseOrThrow(result.balance),
|
||||
exchangeBaseUrl: recoupGroup.exchangeBaseUrl,
|
||||
reserveStatus: WithdrawalGroupStatus.QueryingStatus,
|
||||
reserveStatus: WithdrawalGroupStatus.PendingQueryingStatus,
|
||||
reserveKeyPair: {
|
||||
pub: reservePub,
|
||||
priv: reservePrivMap[reservePub],
|
||||
|
@ -1125,7 +1125,7 @@ export async function autoRefresh(
|
||||
return OperationAttemptResult.finishedEmpty();
|
||||
}
|
||||
|
||||
export function computeRefreshTransactionStatus(
|
||||
export function computeRefreshTransactionState(
|
||||
rg: RefreshGroupRecord,
|
||||
): TransactionState {
|
||||
switch (rg.operationStatus) {
|
||||
@ -1170,7 +1170,7 @@ export async function suspendRefreshGroup(
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
const oldState = computeRefreshTransactionStatus(dg);
|
||||
const oldState = computeRefreshTransactionState(dg);
|
||||
switch (dg.operationStatus) {
|
||||
case RefreshOperationStatus.Finished:
|
||||
return undefined;
|
||||
@ -1179,7 +1179,7 @@ export async function suspendRefreshGroup(
|
||||
await tx.refreshGroups.put(dg);
|
||||
return {
|
||||
oldTxState: oldState,
|
||||
newTxState: computeRefreshTransactionStatus(dg),
|
||||
newTxState: computeRefreshTransactionState(dg),
|
||||
};
|
||||
}
|
||||
case RefreshOperationStatus.Suspended:
|
||||
@ -1215,7 +1215,7 @@ export async function resumeRefreshGroup(
|
||||
);
|
||||
return;
|
||||
}
|
||||
const oldState = computeRefreshTransactionStatus(dg);
|
||||
const oldState = computeRefreshTransactionState(dg);
|
||||
switch (dg.operationStatus) {
|
||||
case RefreshOperationStatus.Finished:
|
||||
return;
|
||||
@ -1227,12 +1227,12 @@ export async function resumeRefreshGroup(
|
||||
await tx.refreshGroups.put(dg);
|
||||
return {
|
||||
oldTxState: oldState,
|
||||
newTxState: computeRefreshTransactionStatus(dg),
|
||||
newTxState: computeRefreshTransactionState(dg),
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
ws.latch.trigger();
|
||||
ws.workAvailable.trigger();
|
||||
if (res) {
|
||||
ws.notify({
|
||||
type: NotificationType.TransactionStateTransition,
|
||||
|
@ -87,14 +87,14 @@ import {
|
||||
} from "./deposits.js";
|
||||
import { getExchangeDetails } from "./exchanges.js";
|
||||
import {
|
||||
abortPay,
|
||||
abortPayMerchant,
|
||||
computePayMerchantTransactionState,
|
||||
expectProposalDownload,
|
||||
extractContractData,
|
||||
processPurchasePay,
|
||||
} from "./pay-merchant.js";
|
||||
import { processPeerPullCredit } from "./pay-peer.js";
|
||||
import { processRefreshGroup } from "./refresh.js";
|
||||
import { computePeerPullCreditTransactionState, computePeerPullDebitTransactionState, computePeerPushCreditTransactionState, computePeerPushDebitTransactionState, processPeerPullCredit } from "./pay-peer.js";
|
||||
import { computeRefreshTransactionState, processRefreshGroup } from "./refresh.js";
|
||||
import { computeTipTransactionStatus, processTip } from "./tip.js";
|
||||
import {
|
||||
abortWithdrawalTransaction,
|
||||
@ -445,7 +445,7 @@ function buildTransactionForPushPaymentDebit(
|
||||
): Transaction {
|
||||
return {
|
||||
type: TransactionType.PeerPushDebit,
|
||||
txState: mkTxStateUnknown(),
|
||||
txState: computePeerPushDebitTransactionState(pi),
|
||||
amountEffective: pi.totalCost,
|
||||
amountRaw: pi.amount,
|
||||
exchangeBaseUrl: pi.exchangeBaseUrl,
|
||||
@ -455,10 +455,10 @@ function buildTransactionForPushPaymentDebit(
|
||||
},
|
||||
frozen: false,
|
||||
extendedStatus:
|
||||
pi.status != PeerPushPaymentInitiationStatus.PurseCreated
|
||||
pi.status != PeerPushPaymentInitiationStatus.Done
|
||||
? ExtendedStatus.Pending
|
||||
: ExtendedStatus.Done,
|
||||
pending: pi.status != PeerPushPaymentInitiationStatus.PurseCreated,
|
||||
pending: pi.status != PeerPushPaymentInitiationStatus.Done,
|
||||
timestamp: pi.timestampCreated,
|
||||
talerUri: constructPayPushUri({
|
||||
exchangeBaseUrl: pi.exchangeBaseUrl,
|
||||
@ -478,7 +478,7 @@ function buildTransactionForPullPaymentDebit(
|
||||
): Transaction {
|
||||
return {
|
||||
type: TransactionType.PeerPullDebit,
|
||||
txState: mkTxStateUnknown(),
|
||||
txState: computePeerPullDebitTransactionState(pi),
|
||||
amountEffective: pi.coinSel?.totalCost
|
||||
? pi.coinSel?.totalCost
|
||||
: Amounts.stringify(pi.contractTerms.amount),
|
||||
@ -528,7 +528,7 @@ function buildTransactionForPeerPullCredit(
|
||||
});
|
||||
return {
|
||||
type: TransactionType.PeerPullCredit,
|
||||
txState: mkTxStateUnknown(),
|
||||
txState: computePeerPullCreditTransactionState(pullCredit),
|
||||
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
|
||||
amountRaw: Amounts.stringify(wsr.instructedAmount),
|
||||
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
||||
@ -563,7 +563,7 @@ function buildTransactionForPeerPullCredit(
|
||||
|
||||
return {
|
||||
type: TransactionType.PeerPullCredit,
|
||||
txState: mkTxStateUnknown(),
|
||||
txState: computePeerPullCreditTransactionState(pullCredit),
|
||||
amountEffective: Amounts.stringify(pullCredit.estimatedAmountEffective),
|
||||
amountRaw: Amounts.stringify(peerContractTerms.amount),
|
||||
exchangeBaseUrl: pullCredit.exchangeBaseUrl,
|
||||
@ -602,7 +602,7 @@ function buildTransactionForPeerPushCredit(
|
||||
|
||||
return {
|
||||
type: TransactionType.PeerPushCredit,
|
||||
txState: mkTxStateUnknown(),
|
||||
txState: computePeerPushCreditTransactionState(pushInc),
|
||||
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
|
||||
amountRaw: Amounts.stringify(wsr.instructedAmount),
|
||||
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
||||
@ -626,7 +626,7 @@ function buildTransactionForPeerPushCredit(
|
||||
|
||||
return {
|
||||
type: TransactionType.PeerPushCredit,
|
||||
txState: mkTxStateUnknown(),
|
||||
txState: computePeerPushCreditTransactionState(pushInc),
|
||||
// FIXME: This is wrong, needs to consider fees!
|
||||
amountEffective: Amounts.stringify(peerContractTerms.amount),
|
||||
amountRaw: Amounts.stringify(peerContractTerms.amount),
|
||||
@ -666,7 +666,7 @@ function buildTransactionForBankIntegratedWithdraw(
|
||||
bankConfirmationUrl: wgRecord.wgInfo.bankInfo.confirmUrl,
|
||||
reserveIsReady:
|
||||
wgRecord.status === WithdrawalGroupStatus.Finished ||
|
||||
wgRecord.status === WithdrawalGroupStatus.Ready,
|
||||
wgRecord.status === WithdrawalGroupStatus.PendingReady,
|
||||
},
|
||||
exchangeBaseUrl: wgRecord.exchangeBaseUrl,
|
||||
extendedStatus: wgRecord.timestampFinish
|
||||
@ -713,7 +713,7 @@ function buildTransactionForManualWithdraw(
|
||||
exchangePaytoUris,
|
||||
reserveIsReady:
|
||||
withdrawalGroup.status === WithdrawalGroupStatus.Finished ||
|
||||
withdrawalGroup.status === WithdrawalGroupStatus.Ready,
|
||||
withdrawalGroup.status === WithdrawalGroupStatus.PendingReady,
|
||||
},
|
||||
exchangeBaseUrl: withdrawalGroup.exchangeBaseUrl,
|
||||
extendedStatus: withdrawalGroup.timestampFinish
|
||||
@ -753,7 +753,7 @@ function buildTransactionForRefresh(
|
||||
).amount;
|
||||
return {
|
||||
type: TransactionType.Refresh,
|
||||
txState: mkTxStateUnknown(),
|
||||
txState: computeRefreshTransactionState(refreshGroupRecord),
|
||||
refreshReason: refreshGroupRecord.reason,
|
||||
amountEffective: Amounts.stringify(
|
||||
Amounts.zeroOfCurrency(refreshGroupRecord.currency),
|
||||
@ -1538,7 +1538,7 @@ export async function retryTransaction(
|
||||
switch (parsedTx.tag) {
|
||||
case TransactionType.PeerPullCredit: {
|
||||
const taskId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.PeerPullInitiation,
|
||||
tag: PendingTaskType.PeerPullCredit,
|
||||
pursePub: parsedTx.pursePub,
|
||||
});
|
||||
await resetOperationTimeout(ws, taskId);
|
||||
@ -1866,7 +1866,7 @@ export async function abortTransaction(
|
||||
|
||||
switch (txId.tag) {
|
||||
case TransactionType.Payment: {
|
||||
await abortPay(ws, txId.proposalId);
|
||||
await abortPayMerchant(ws, txId.proposalId);
|
||||
break;
|
||||
}
|
||||
case TransactionType.Withdrawal: {
|
||||
|
@ -135,6 +135,7 @@ import {
|
||||
notifyTransition,
|
||||
stopLongpolling,
|
||||
} from "./transactions.js";
|
||||
import { assertUnreachable } from "../util/assertUnreachable.js";
|
||||
|
||||
/**
|
||||
* Logger for this file.
|
||||
@ -160,25 +161,25 @@ export async function suspendWithdrawalTransaction(
|
||||
}
|
||||
let newStatus: WithdrawalGroupStatus | undefined = undefined;
|
||||
switch (wg.status) {
|
||||
case WithdrawalGroupStatus.Ready:
|
||||
case WithdrawalGroupStatus.PendingReady:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedReady;
|
||||
break;
|
||||
case WithdrawalGroupStatus.AbortingBank:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedAbortingBank;
|
||||
break;
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.PendingWaitConfirmBank:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedWaitConfirmBank;
|
||||
break;
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
case WithdrawalGroupStatus.PendingRegisteringBank:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedRegisteringBank;
|
||||
break;
|
||||
case WithdrawalGroupStatus.QueryingStatus:
|
||||
newStatus = WithdrawalGroupStatus.QueryingStatus;
|
||||
case WithdrawalGroupStatus.PendingQueryingStatus:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedQueryingStatus;
|
||||
break;
|
||||
case WithdrawalGroupStatus.Kyc:
|
||||
case WithdrawalGroupStatus.PendingKyc:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedKyc;
|
||||
break;
|
||||
case WithdrawalGroupStatus.Aml:
|
||||
case WithdrawalGroupStatus.PendingAml:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedAml;
|
||||
break;
|
||||
default:
|
||||
@ -221,25 +222,25 @@ export async function resumeWithdrawalTransaction(
|
||||
let newStatus: WithdrawalGroupStatus | undefined = undefined;
|
||||
switch (wg.status) {
|
||||
case WithdrawalGroupStatus.SuspendedReady:
|
||||
newStatus = WithdrawalGroupStatus.Ready;
|
||||
newStatus = WithdrawalGroupStatus.PendingReady;
|
||||
break;
|
||||
case WithdrawalGroupStatus.SuspendedAbortingBank:
|
||||
newStatus = WithdrawalGroupStatus.AbortingBank;
|
||||
break;
|
||||
case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
|
||||
newStatus = WithdrawalGroupStatus.WaitConfirmBank;
|
||||
newStatus = WithdrawalGroupStatus.PendingWaitConfirmBank;
|
||||
break;
|
||||
case WithdrawalGroupStatus.SuspendedQueryingStatus:
|
||||
newStatus = WithdrawalGroupStatus.QueryingStatus;
|
||||
newStatus = WithdrawalGroupStatus.PendingQueryingStatus;
|
||||
break;
|
||||
case WithdrawalGroupStatus.SuspendedRegisteringBank:
|
||||
newStatus = WithdrawalGroupStatus.RegisteringBank;
|
||||
newStatus = WithdrawalGroupStatus.PendingRegisteringBank;
|
||||
break;
|
||||
case WithdrawalGroupStatus.SuspendedAml:
|
||||
newStatus = WithdrawalGroupStatus.Aml;
|
||||
newStatus = WithdrawalGroupStatus.PendingAml;
|
||||
break;
|
||||
case WithdrawalGroupStatus.SuspendedKyc:
|
||||
newStatus = WithdrawalGroupStatus.Kyc;
|
||||
newStatus = WithdrawalGroupStatus.PendingKyc;
|
||||
break;
|
||||
default:
|
||||
logger.warn(
|
||||
@ -289,21 +290,21 @@ export async function abortWithdrawalTransaction(
|
||||
}
|
||||
let newStatus: WithdrawalGroupStatus | undefined = undefined;
|
||||
switch (wg.status) {
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
case WithdrawalGroupStatus.PendingWaitConfirmBank:
|
||||
case WithdrawalGroupStatus.PendingRegisteringBank:
|
||||
case WithdrawalGroupStatus.AbortingBank:
|
||||
newStatus = WithdrawalGroupStatus.AbortingBank;
|
||||
break;
|
||||
case WithdrawalGroupStatus.Aml:
|
||||
case WithdrawalGroupStatus.PendingAml:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedAml;
|
||||
break;
|
||||
case WithdrawalGroupStatus.Kyc:
|
||||
case WithdrawalGroupStatus.PendingKyc:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedKyc;
|
||||
break;
|
||||
case WithdrawalGroupStatus.QueryingStatus:
|
||||
case WithdrawalGroupStatus.PendingQueryingStatus:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedQueryingStatus;
|
||||
break;
|
||||
case WithdrawalGroupStatus.Ready:
|
||||
case WithdrawalGroupStatus.PendingReady:
|
||||
newStatus = WithdrawalGroupStatus.SuspendedReady;
|
||||
break;
|
||||
case WithdrawalGroupStatus.SuspendedAbortingBank:
|
||||
@ -316,9 +317,13 @@ export async function abortWithdrawalTransaction(
|
||||
case WithdrawalGroupStatus.SuspendedRegisteringBank:
|
||||
case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
|
||||
case WithdrawalGroupStatus.Finished:
|
||||
case WithdrawalGroupStatus.BankAborted:
|
||||
case WithdrawalGroupStatus.FailedBankAborted:
|
||||
case WithdrawalGroupStatus.AbortedExchange:
|
||||
case WithdrawalGroupStatus.FailedAbortingBank:
|
||||
// Not allowed
|
||||
break;
|
||||
default:
|
||||
assertUnreachable(wg.status);
|
||||
}
|
||||
if (newStatus != null) {
|
||||
const oldTxState = computeWithdrawalTransactionStatus(wg);
|
||||
@ -385,7 +390,7 @@ export function computeWithdrawalTransactionStatus(
|
||||
wgRecord: WithdrawalGroupRecord,
|
||||
): TransactionState {
|
||||
switch (wgRecord.status) {
|
||||
case WithdrawalGroupStatus.BankAborted:
|
||||
case WithdrawalGroupStatus.FailedBankAborted:
|
||||
return {
|
||||
major: TransactionMajorState.Aborted,
|
||||
};
|
||||
@ -393,22 +398,22 @@ export function computeWithdrawalTransactionStatus(
|
||||
return {
|
||||
major: TransactionMajorState.Done,
|
||||
};
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
case WithdrawalGroupStatus.PendingRegisteringBank:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.BankRegisterReserve,
|
||||
};
|
||||
case WithdrawalGroupStatus.Ready:
|
||||
case WithdrawalGroupStatus.PendingReady:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.WithdrawCoins,
|
||||
};
|
||||
case WithdrawalGroupStatus.QueryingStatus:
|
||||
case WithdrawalGroupStatus.PendingQueryingStatus:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.ExchangeWaitReserve,
|
||||
};
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.PendingWaitConfirmBank:
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.BankConfirmTransfer,
|
||||
@ -444,13 +449,13 @@ export function computeWithdrawalTransactionStatus(
|
||||
minor: TransactionMinorState.WithdrawCoins,
|
||||
};
|
||||
}
|
||||
case WithdrawalGroupStatus.Aml: {
|
||||
case WithdrawalGroupStatus.PendingAml: {
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.AmlRequired,
|
||||
};
|
||||
}
|
||||
case WithdrawalGroupStatus.Kyc: {
|
||||
case WithdrawalGroupStatus.PendingKyc: {
|
||||
return {
|
||||
major: TransactionMajorState.Pending,
|
||||
minor: TransactionMinorState.KycRequired,
|
||||
@ -473,6 +478,11 @@ export function computeWithdrawalTransactionStatus(
|
||||
major: TransactionMajorState.Failed,
|
||||
minor: TransactionMinorState.AbortingBank,
|
||||
};
|
||||
case WithdrawalGroupStatus.AbortedExchange:
|
||||
return {
|
||||
major: TransactionMajorState.Aborted,
|
||||
minor: TransactionMinorState.Exchange,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1122,7 +1132,7 @@ async function queryReserve(
|
||||
withdrawalGroupId,
|
||||
});
|
||||
checkDbInvariant(!!withdrawalGroup);
|
||||
if (withdrawalGroup.status !== WithdrawalGroupStatus.QueryingStatus) {
|
||||
if (withdrawalGroup.status !== WithdrawalGroupStatus.PendingQueryingStatus) {
|
||||
return { ready: true };
|
||||
}
|
||||
const reservePub = withdrawalGroup.reservePub;
|
||||
@ -1135,7 +1145,7 @@ async function queryReserve(
|
||||
|
||||
logger.info(`querying reserve status via ${reserveUrl.href}`);
|
||||
|
||||
const resp = await ws.http.get(reserveUrl.href, {
|
||||
const resp = await ws.http.fetch(reserveUrl.href, {
|
||||
timeout: getReserveRequestTimeout(withdrawalGroup),
|
||||
cancellationToken,
|
||||
});
|
||||
@ -1177,7 +1187,7 @@ async function queryReserve(
|
||||
return undefined;
|
||||
}
|
||||
const txStateOld = computeWithdrawalTransactionStatus(wg);
|
||||
wg.status = WithdrawalGroupStatus.Ready;
|
||||
wg.status = WithdrawalGroupStatus.PendingReady;
|
||||
const txStateNew = computeWithdrawalTransactionStatus(wg);
|
||||
wg.reserveBalanceAmount = Amounts.stringify(result.response.balance);
|
||||
await tx.withdrawalGroups.put(wg);
|
||||
@ -1250,12 +1260,12 @@ export async function processWithdrawalGroup(
|
||||
}
|
||||
|
||||
switch (withdrawalGroup.status) {
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
case WithdrawalGroupStatus.PendingRegisteringBank:
|
||||
await processReserveBankStatus(ws, withdrawalGroupId);
|
||||
return await processWithdrawalGroup(ws, withdrawalGroupId, {
|
||||
forceNow: true,
|
||||
});
|
||||
case WithdrawalGroupStatus.QueryingStatus: {
|
||||
case WithdrawalGroupStatus.PendingQueryingStatus: {
|
||||
runLongpollAsync(ws, retryTag, (ct) => {
|
||||
return queryReserve(ws, withdrawalGroupId, ct);
|
||||
});
|
||||
@ -1266,7 +1276,7 @@ export async function processWithdrawalGroup(
|
||||
type: OperationAttemptResultType.Longpoll,
|
||||
};
|
||||
}
|
||||
case WithdrawalGroupStatus.WaitConfirmBank: {
|
||||
case WithdrawalGroupStatus.PendingWaitConfirmBank: {
|
||||
const res = await processReserveBankStatus(ws, withdrawalGroupId);
|
||||
switch (res.status) {
|
||||
case BankStatusResultCode.Aborted:
|
||||
@ -1284,7 +1294,7 @@ export async function processWithdrawalGroup(
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WithdrawalGroupStatus.BankAborted: {
|
||||
case WithdrawalGroupStatus.FailedBankAborted: {
|
||||
// FIXME
|
||||
return {
|
||||
type: OperationAttemptResultType.Pending,
|
||||
@ -1294,7 +1304,7 @@ export async function processWithdrawalGroup(
|
||||
case WithdrawalGroupStatus.Finished:
|
||||
// We can try to withdraw, nothing needs to be done with the reserve.
|
||||
break;
|
||||
case WithdrawalGroupStatus.Ready:
|
||||
case WithdrawalGroupStatus.PendingReady:
|
||||
// Continue with the actual withdrawal!
|
||||
break;
|
||||
default:
|
||||
@ -1847,8 +1857,8 @@ async function registerReserveWithBank(
|
||||
withdrawalGroupId,
|
||||
});
|
||||
switch (withdrawalGroup?.status) {
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
case WithdrawalGroupStatus.PendingWaitConfirmBank:
|
||||
case WithdrawalGroupStatus.PendingRegisteringBank:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@ -1885,8 +1895,8 @@ async function registerReserveWithBank(
|
||||
return undefined;
|
||||
}
|
||||
switch (r.status) {
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.PendingRegisteringBank:
|
||||
case WithdrawalGroupStatus.PendingWaitConfirmBank:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@ -1898,7 +1908,7 @@ async function registerReserveWithBank(
|
||||
AbsoluteTime.now(),
|
||||
);
|
||||
const oldTxState = computeWithdrawalTransactionStatus(r);
|
||||
r.status = WithdrawalGroupStatus.WaitConfirmBank;
|
||||
r.status = WithdrawalGroupStatus.PendingWaitConfirmBank;
|
||||
const newTxState = computeWithdrawalTransactionStatus(r);
|
||||
await tx.withdrawalGroups.put(r);
|
||||
return {
|
||||
@ -1928,8 +1938,8 @@ async function processReserveBankStatus(
|
||||
withdrawalGroupId,
|
||||
});
|
||||
switch (withdrawalGroup?.status) {
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
case WithdrawalGroupStatus.PendingWaitConfirmBank:
|
||||
case WithdrawalGroupStatus.PendingRegisteringBank:
|
||||
break;
|
||||
default:
|
||||
return {
|
||||
@ -1969,8 +1979,8 @@ async function processReserveBankStatus(
|
||||
return;
|
||||
}
|
||||
switch (r.status) {
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.PendingRegisteringBank:
|
||||
case WithdrawalGroupStatus.PendingWaitConfirmBank:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@ -1981,7 +1991,7 @@ async function processReserveBankStatus(
|
||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
const oldTxState = computeWithdrawalTransactionStatus(r);
|
||||
r.wgInfo.bankInfo.timestampBankConfirmed = now;
|
||||
r.status = WithdrawalGroupStatus.BankAborted;
|
||||
r.status = WithdrawalGroupStatus.FailedBankAborted;
|
||||
const newTxState = computeWithdrawalTransactionStatus(r);
|
||||
await tx.withdrawalGroups.put(r);
|
||||
return {
|
||||
@ -2002,7 +2012,7 @@ async function processReserveBankStatus(
|
||||
}
|
||||
|
||||
// FIXME: Why do we do this?!
|
||||
if (withdrawalGroup.status === WithdrawalGroupStatus.RegisteringBank) {
|
||||
if (withdrawalGroup.status === WithdrawalGroupStatus.PendingRegisteringBank) {
|
||||
await registerReserveWithBank(ws, withdrawalGroupId);
|
||||
return await processReserveBankStatus(ws, withdrawalGroupId);
|
||||
}
|
||||
@ -2016,8 +2026,8 @@ async function processReserveBankStatus(
|
||||
}
|
||||
// Re-check reserve status within transaction
|
||||
switch (r.status) {
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.PendingRegisteringBank:
|
||||
case WithdrawalGroupStatus.PendingWaitConfirmBank:
|
||||
break;
|
||||
default:
|
||||
return undefined;
|
||||
@ -2030,7 +2040,7 @@ async function processReserveBankStatus(
|
||||
logger.info("withdrawal: transfer confirmed by bank.");
|
||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
r.wgInfo.bankInfo.timestampBankConfirmed = now;
|
||||
r.status = WithdrawalGroupStatus.QueryingStatus;
|
||||
r.status = WithdrawalGroupStatus.PendingQueryingStatus;
|
||||
// FIXME: Notification is deprecated with DD37.
|
||||
ws.notify({
|
||||
type: NotificationType.WithdrawalGroupBankConfirmed,
|
||||
@ -2276,7 +2286,7 @@ export async function acceptWithdrawalFromUri(
|
||||
},
|
||||
restrictAge: req.restrictAge,
|
||||
forcedDenomSel: req.forcedDenomSel,
|
||||
reserveStatus: WithdrawalGroupStatus.RegisteringBank,
|
||||
reserveStatus: WithdrawalGroupStatus.PendingRegisteringBank,
|
||||
});
|
||||
|
||||
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
|
||||
@ -2291,19 +2301,14 @@ export async function acceptWithdrawalFromUri(
|
||||
const processedWithdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, {
|
||||
withdrawalGroupId,
|
||||
});
|
||||
if (processedWithdrawalGroup?.status === WithdrawalGroupStatus.BankAborted) {
|
||||
if (processedWithdrawalGroup?.status === WithdrawalGroupStatus.FailedBankAborted) {
|
||||
throw TalerError.fromDetail(
|
||||
TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK,
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
// Start withdrawal in the background
|
||||
processWithdrawalGroup(ws, withdrawalGroupId, {
|
||||
forceNow: true,
|
||||
}).catch((err) => {
|
||||
logger.error("Processing withdrawal (after creation) failed:", err);
|
||||
});
|
||||
ws.workAvailable.trigger();
|
||||
|
||||
return {
|
||||
reservePub: withdrawalGroup.reservePub,
|
||||
@ -2337,7 +2342,7 @@ export async function createManualWithdrawal(
|
||||
exchangeBaseUrl: req.exchangeBaseUrl,
|
||||
forcedDenomSel: req.forcedDenomSel,
|
||||
restrictAge: req.restrictAge,
|
||||
reserveStatus: WithdrawalGroupStatus.QueryingStatus,
|
||||
reserveStatus: WithdrawalGroupStatus.PendingQueryingStatus,
|
||||
});
|
||||
|
||||
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
|
||||
@ -2357,18 +2362,7 @@ export async function createManualWithdrawal(
|
||||
return await getFundingPaytoUris(tx, withdrawalGroup.withdrawalGroupId);
|
||||
});
|
||||
|
||||
// Start withdrawal in the background (do not await!)
|
||||
// FIXME: We could also interrupt the task look if it is waiting and
|
||||
// rely on retry handling to re-process the withdrawal group.
|
||||
runOperationWithErrorReporting(
|
||||
ws,
|
||||
TaskIdentifiers.forWithdrawal(withdrawalGroup),
|
||||
async () => {
|
||||
return await processWithdrawalGroup(ws, withdrawalGroupId, {
|
||||
forceNow: true,
|
||||
});
|
||||
},
|
||||
);
|
||||
ws.workAvailable.trigger();
|
||||
|
||||
return {
|
||||
reservePub: withdrawalGroup.reservePub,
|
||||
|
@ -37,9 +37,8 @@ export enum PendingTaskType {
|
||||
Withdraw = "withdraw",
|
||||
Deposit = "deposit",
|
||||
Backup = "backup",
|
||||
// FIXME: Rename to peer-push-debit and peer-pull-debit
|
||||
PeerPushInitiation = "peer-push-initiation",
|
||||
PeerPullInitiation = "peer-pull-initiation",
|
||||
PeerPushDebit = "peer-push-debit",
|
||||
PeerPullCredit = "peer-pull-credit",
|
||||
PeerPushCredit = "peer-push-credit",
|
||||
PeerPullDebit = "peer-pull-debit",
|
||||
}
|
||||
@ -83,7 +82,7 @@ export interface PendingExchangeUpdateTask {
|
||||
* The wallet wants to send a peer push payment.
|
||||
*/
|
||||
export interface PendingPeerPushInitiationTask {
|
||||
type: PendingTaskType.PeerPushInitiation;
|
||||
type: PendingTaskType.PeerPushDebit;
|
||||
pursePub: string;
|
||||
}
|
||||
|
||||
@ -91,7 +90,7 @@ export interface PendingPeerPushInitiationTask {
|
||||
* The wallet wants to send a peer pull payment.
|
||||
*/
|
||||
export interface PendingPeerPullInitiationTask {
|
||||
type: PendingTaskType.PeerPullInitiation;
|
||||
type: PendingTaskType.PeerPullCredit;
|
||||
pursePub: string;
|
||||
}
|
||||
|
||||
|
@ -197,9 +197,9 @@ export type ParsedTaskIdentifier =
|
||||
| { tag: PendingTaskType.ExchangeCheckRefresh; exchangeBaseUrl: string }
|
||||
| { tag: PendingTaskType.ExchangeUpdate; exchangeBaseUrl: string }
|
||||
| { tag: PendingTaskType.PeerPullDebit; peerPullPaymentIncomingId: string }
|
||||
| { tag: PendingTaskType.PeerPullInitiation; pursePub: string }
|
||||
| { tag: PendingTaskType.PeerPullCredit; pursePub: string }
|
||||
| { tag: PendingTaskType.PeerPushCredit; peerPushPaymentIncomingId: string }
|
||||
| { tag: PendingTaskType.PeerPushInitiation; pursePub: string }
|
||||
| { tag: PendingTaskType.PeerPushDebit; pursePub: string }
|
||||
| { tag: PendingTaskType.Purchase; proposalId: string }
|
||||
| { tag: PendingTaskType.Recoup; recoupGroupId: string }
|
||||
| { tag: PendingTaskType.TipPickup; walletTipId: string }
|
||||
@ -223,9 +223,9 @@ export function constructTaskIdentifier(p: ParsedTaskIdentifier): string {
|
||||
return `${p.tag}:${p.peerPullPaymentIncomingId}`;
|
||||
case PendingTaskType.PeerPushCredit:
|
||||
return `${p.tag}:${p.peerPushPaymentIncomingId}`;
|
||||
case PendingTaskType.PeerPullInitiation:
|
||||
case PendingTaskType.PeerPullCredit:
|
||||
return `${p.tag}:${p.pursePub}`;
|
||||
case PendingTaskType.PeerPushInitiation:
|
||||
case PendingTaskType.PeerPushDebit:
|
||||
return `${p.tag}:${p.pursePub}`;
|
||||
case PendingTaskType.Purchase:
|
||||
return `${p.tag}:${p.proposalId}`;
|
||||
@ -276,12 +276,12 @@ export namespace TaskIdentifiers {
|
||||
export function forPeerPushPaymentInitiation(
|
||||
ppi: PeerPushPaymentInitiationRecord,
|
||||
): string {
|
||||
return `${PendingTaskType.PeerPushInitiation}:${ppi.pursePub}`;
|
||||
return `${PendingTaskType.PeerPushDebit}:${ppi.pursePub}`;
|
||||
}
|
||||
export function forPeerPullPaymentInitiation(
|
||||
ppi: PeerPullPaymentInitiationRecord,
|
||||
): string {
|
||||
return `${PendingTaskType.PeerPullInitiation}:${ppi.pursePub}`;
|
||||
return `${PendingTaskType.PeerPullCredit}:${ppi.pursePub}`;
|
||||
}
|
||||
export function forPeerPullPaymentDebit(
|
||||
ppi: PeerPullPaymentIncomingRecord,
|
||||
|
@ -208,7 +208,7 @@ import {
|
||||
processPeerPullCredit,
|
||||
processPeerPullDebit,
|
||||
processPeerPushCredit,
|
||||
processPeerPushInitiation,
|
||||
processPeerPushDebit,
|
||||
} from "./operations/pay-peer.js";
|
||||
import { getPendingOperations } from "./operations/pending.js";
|
||||
import {
|
||||
@ -314,9 +314,9 @@ async function callOperationHandler(
|
||||
}
|
||||
case PendingTaskType.Backup:
|
||||
return await processBackupForProvider(ws, pending.backupProviderBaseUrl);
|
||||
case PendingTaskType.PeerPushInitiation:
|
||||
return await processPeerPushInitiation(ws, pending.pursePub);
|
||||
case PendingTaskType.PeerPullInitiation:
|
||||
case PendingTaskType.PeerPushDebit:
|
||||
return await processPeerPushDebit(ws, pending.pursePub);
|
||||
case PendingTaskType.PeerPullCredit:
|
||||
return await processPeerPullCredit(ws, pending.pursePub);
|
||||
case PendingTaskType.PeerPullDebit:
|
||||
return await processPeerPullDebit(ws, pending.peerPullPaymentIncomingId);
|
||||
@ -439,7 +439,7 @@ async function runTaskLoop(
|
||||
});
|
||||
// Wait until either the timeout, or we are notified (via the latch)
|
||||
// that more work might be available.
|
||||
await Promise.race([timeout, ws.latch.wait()]);
|
||||
await Promise.race([timeout, ws.workAvailable.wait()]);
|
||||
} else {
|
||||
logger.trace(
|
||||
`running ${pending.pendingOperations.length} pending operations`,
|
||||
@ -1659,7 +1659,7 @@ class InternalWalletStateImpl implements InternalWalletState {
|
||||
merchantInfoCache: Record<string, MerchantInfo> = {};
|
||||
|
||||
readonly timerGroup: TimerGroup;
|
||||
latch = new AsyncCondition();
|
||||
workAvailable = new AsyncCondition();
|
||||
stopped = false;
|
||||
|
||||
listeners: NotificationListener[] = [];
|
||||
|
Loading…
Reference in New Issue
Block a user