wallet-harness: make sure events are not lost in deposit test
This commit is contained in:
parent
fc2adae6bd
commit
e81ae0f3e5
@ -2062,6 +2062,7 @@ export class WalletService {
|
|||||||
[
|
[
|
||||||
"--wallet-db",
|
"--wallet-db",
|
||||||
dbPath,
|
dbPath,
|
||||||
|
"-LDEBUG", // FIXME: Make this configurable?
|
||||||
"--no-throttle", // FIXME: Optionally do throttling for some tests?
|
"--no-throttle", // FIXME: Optionally do throttling for some tests?
|
||||||
"advanced",
|
"advanced",
|
||||||
"serve",
|
"serve",
|
||||||
|
@ -45,9 +45,17 @@ export async function runDepositTest(t: GlobalTestState) {
|
|||||||
|
|
||||||
await withdrawalResult.withdrawalFinishedCond;
|
await withdrawalResult.withdrawalFinishedCond;
|
||||||
|
|
||||||
const depositDone = await walletClient.waitForNotificationCond(
|
const dgIdResp = await walletClient.client.call(
|
||||||
|
WalletApiOperation.GenerateDepositGroupTxId,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
const depositTxId = dgIdResp.transactionId;
|
||||||
|
|
||||||
|
const depositDone = walletClient.waitForNotificationCond(
|
||||||
(n) =>
|
(n) =>
|
||||||
n.type == NotificationType.TransactionStateTransition &&
|
n.type == NotificationType.TransactionStateTransition &&
|
||||||
|
n.transactionId == depositTxId &&
|
||||||
n.newTxState == TransactionState.Done,
|
n.newTxState == TransactionState.Done,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -56,9 +64,14 @@ export async function runDepositTest(t: GlobalTestState) {
|
|||||||
{
|
{
|
||||||
amount: "TESTKUDOS:10",
|
amount: "TESTKUDOS:10",
|
||||||
depositPaytoUri: getPayto("foo"),
|
depositPaytoUri: getPayto("foo"),
|
||||||
|
transactionId: depositTxId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
t.assertDeepEqual(depositGroupResult.transactionId, depositTxId);
|
||||||
|
|
||||||
|
await depositDone;
|
||||||
|
|
||||||
const transactions = await walletClient.client.call(
|
const transactions = await walletClient.client.call(
|
||||||
WalletApiOperation.GetTransactions,
|
WalletApiOperation.GetTransactions,
|
||||||
{},
|
{},
|
||||||
|
@ -1708,6 +1708,13 @@ export interface DepositGroupFees {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateDepositGroupRequest {
|
export interface CreateDepositGroupRequest {
|
||||||
|
/**
|
||||||
|
* Pre-allocated transaction ID.
|
||||||
|
* Allows clients to easily handle notifications
|
||||||
|
* that occur while the operation has been created but
|
||||||
|
* before the creation request has returned.
|
||||||
|
*/
|
||||||
|
transactionId?: string;
|
||||||
depositPaytoUri: string;
|
depositPaytoUri: string;
|
||||||
amount: AmountString;
|
amount: AmountString;
|
||||||
}
|
}
|
||||||
@ -1733,6 +1740,7 @@ export const codecForCreateDepositGroupRequest =
|
|||||||
buildCodecForObject<CreateDepositGroupRequest>()
|
buildCodecForObject<CreateDepositGroupRequest>()
|
||||||
.property("amount", codecForAmountString())
|
.property("amount", codecForAmountString())
|
||||||
.property("depositPaytoUri", codecForString())
|
.property("depositPaytoUri", codecForString())
|
||||||
|
.property("transactionId", codecOptional(codecForString()))
|
||||||
.build("CreateDepositGroupRequest");
|
.build("CreateDepositGroupRequest");
|
||||||
|
|
||||||
export interface CreateDepositGroupResponse {
|
export interface CreateDepositGroupResponse {
|
||||||
|
@ -865,6 +865,7 @@ export enum DepositGroupOperationStatus {
|
|||||||
AbortingWithRefresh = 11 /* ACTIVE_START + 1 */,
|
AbortingWithRefresh = 11 /* ACTIVE_START + 1 */,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Improve name! This enum is very specific to deposits.
|
||||||
export enum TransactionStatus {
|
export enum TransactionStatus {
|
||||||
Unknown = 10,
|
Unknown = 10,
|
||||||
Accepted = 20,
|
Accepted = 20,
|
||||||
@ -1380,6 +1381,7 @@ export type WgInfo =
|
|||||||
| WgInfoBankRecoup;
|
| WgInfoBankRecoup;
|
||||||
|
|
||||||
export type KycUserType = "individual" | "business";
|
export type KycUserType = "individual" | "business";
|
||||||
|
|
||||||
export interface KycPendingInfo {
|
export interface KycPendingInfo {
|
||||||
paytoHash: string;
|
paytoHash: string;
|
||||||
requirementRow: number;
|
requirementRow: number;
|
||||||
|
@ -67,7 +67,7 @@ import { getTotalRefreshCost, KycPendingInfo, KycUserType } from "../index.js";
|
|||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
|
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
|
||||||
import { OperationAttemptResult } from "../util/retries.js";
|
import { OperationAttemptResult } from "../util/retries.js";
|
||||||
import { makeTransactionId, spendCoins } from "./common.js";
|
import { spendCoins } from "./common.js";
|
||||||
import { getExchangeDetails } from "./exchanges.js";
|
import { getExchangeDetails } from "./exchanges.js";
|
||||||
import {
|
import {
|
||||||
extractContractData,
|
extractContractData,
|
||||||
@ -75,13 +75,20 @@ import {
|
|||||||
getTotalPaymentCost,
|
getTotalPaymentCost,
|
||||||
} from "./pay-merchant.js";
|
} from "./pay-merchant.js";
|
||||||
import { selectPayCoinsNew } from "../util/coinSelection.js";
|
import { selectPayCoinsNew } from "../util/coinSelection.js";
|
||||||
import { constructTransactionIdentifier } from "./transactions.js";
|
import {
|
||||||
|
constructTransactionIdentifier,
|
||||||
|
parseTransactionIdentifier,
|
||||||
|
} from "./transactions.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger.
|
* Logger.
|
||||||
*/
|
*/
|
||||||
const logger = new Logger("deposits.ts");
|
const logger = new Logger("deposits.ts");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the (DD37-style) transaction status based on the
|
||||||
|
* database record of a deposit group.
|
||||||
|
*/
|
||||||
export async function computeDepositTransactionStatus(
|
export async function computeDepositTransactionStatus(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
dg: DepositGroupRecord,
|
dg: DepositGroupRecord,
|
||||||
@ -151,7 +158,8 @@ export async function abortDepositGroup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check KYC status with the exchange, throw an appropriate exception when KYC is required.
|
* Check KYC status with the exchange, throw an appropriate exception when KYC
|
||||||
|
* is required.
|
||||||
*
|
*
|
||||||
* FIXME: Why does this throw an exception when KYC is required?
|
* FIXME: Why does this throw an exception when KYC is required?
|
||||||
* Should we not return some proper result record here?
|
* Should we not return some proper result record here?
|
||||||
@ -221,6 +229,7 @@ export async function processDepositGroup(
|
|||||||
|
|
||||||
// Check for cancellation before expensive operations.
|
// Check for cancellation before expensive operations.
|
||||||
options.cancellationToken?.throwIfCancelled();
|
options.cancellationToken?.throwIfCancelled();
|
||||||
|
// FIXME: Cache these!
|
||||||
const depositPermissions = await generateDepositPermissions(
|
const depositPermissions = await generateDepositPermissions(
|
||||||
ws,
|
ws,
|
||||||
depositGroup.payCoinSelection,
|
depositGroup.payCoinSelection,
|
||||||
@ -438,7 +447,7 @@ async function trackDepositPermission(
|
|||||||
wireHash,
|
wireHash,
|
||||||
});
|
});
|
||||||
url.searchParams.set("merchant_sig", sigResp.sig);
|
url.searchParams.set("merchant_sig", sigResp.sig);
|
||||||
const httpResp = await ws.http.get(url.href);
|
const httpResp = await ws.http.fetch(url.href, { method: "GET" });
|
||||||
switch (httpResp.status) {
|
switch (httpResp.status) {
|
||||||
case HttpStatusCode.Accepted: {
|
case HttpStatusCode.Accepted: {
|
||||||
const accepted = await readSuccessResponseJsonOrThrow(
|
const accepted = await readSuccessResponseJsonOrThrow(
|
||||||
@ -463,6 +472,9 @@ async function trackDepositPermission(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Check if creating a deposit group is possible and calculate
|
||||||
|
* the associated fees.
|
||||||
|
*
|
||||||
* FIXME: This should be renamed to checkDepositGroup,
|
* FIXME: This should be renamed to checkDepositGroup,
|
||||||
* as it doesn't prepare anything
|
* as it doesn't prepare anything
|
||||||
*/
|
*/
|
||||||
@ -671,9 +683,18 @@ export async function createDepositGroup(
|
|||||||
|
|
||||||
const totalDepositCost = await getTotalPaymentCost(ws, payCoinSel.coinSel);
|
const totalDepositCost = await getTotalPaymentCost(ws, payCoinSel.coinSel);
|
||||||
|
|
||||||
const depositGroupId = encodeCrock(getRandomBytes(32));
|
let depositGroupId: string;
|
||||||
|
if (req.transactionId) {
|
||||||
|
const txId = parseTransactionIdentifier(req.transactionId);
|
||||||
|
if (!txId || txId.tag !== TransactionType.Deposit) {
|
||||||
|
throw Error("invalid transaction ID");
|
||||||
|
}
|
||||||
|
depositGroupId = txId.depositGroupId;
|
||||||
|
} else {
|
||||||
|
depositGroupId = encodeCrock(getRandomBytes(32));
|
||||||
|
}
|
||||||
|
|
||||||
const countarpartyEffectiveDepositAmount =
|
const counterpartyEffectiveDepositAmount =
|
||||||
await getCounterpartyEffectiveDepositAmount(
|
await getCounterpartyEffectiveDepositAmount(
|
||||||
ws,
|
ws,
|
||||||
p.targetType,
|
p.targetType,
|
||||||
@ -698,7 +719,7 @@ export async function createDepositGroup(
|
|||||||
merchantPub: merchantPair.pub,
|
merchantPub: merchantPair.pub,
|
||||||
totalPayCost: Amounts.stringify(totalDepositCost),
|
totalPayCost: Amounts.stringify(totalDepositCost),
|
||||||
effectiveDepositAmount: Amounts.stringify(
|
effectiveDepositAmount: Amounts.stringify(
|
||||||
countarpartyEffectiveDepositAmount,
|
counterpartyEffectiveDepositAmount,
|
||||||
),
|
),
|
||||||
wire: {
|
wire: {
|
||||||
payto_uri: req.depositPaytoUri,
|
payto_uri: req.depositPaytoUri,
|
||||||
@ -707,6 +728,11 @@ export async function createDepositGroup(
|
|||||||
operationStatus: OperationStatus.Pending,
|
operationStatus: OperationStatus.Pending,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const transactionId = constructTransactionIdentifier({
|
||||||
|
tag: TransactionType.Deposit,
|
||||||
|
depositGroupId,
|
||||||
|
});
|
||||||
|
|
||||||
await ws.db
|
await ws.db
|
||||||
.mktx((x) => [
|
.mktx((x) => [
|
||||||
x.depositGroups,
|
x.depositGroups,
|
||||||
@ -718,7 +744,7 @@ export async function createDepositGroup(
|
|||||||
])
|
])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
await spendCoins(ws, tx, {
|
await spendCoins(ws, tx, {
|
||||||
allocationId: `txn:deposit:${depositGroup.depositGroupId}`,
|
allocationId: transactionId,
|
||||||
coinPubs: payCoinSel.coinSel.coinPubs,
|
coinPubs: payCoinSel.coinSel.coinPubs,
|
||||||
contributions: payCoinSel.coinSel.coinContributions.map((x) =>
|
contributions: payCoinSel.coinSel.coinContributions.map((x) =>
|
||||||
Amounts.parseOrThrow(x),
|
Amounts.parseOrThrow(x),
|
||||||
@ -729,8 +755,8 @@ export async function createDepositGroup(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
depositGroupId: depositGroupId,
|
depositGroupId,
|
||||||
transactionId: makeTransactionId(TransactionType.Deposit, depositGroupId),
|
transactionId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1459,7 +1459,9 @@ export async function processPurchasePay(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], () =>
|
const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], () =>
|
||||||
ws.http.postJson(payUrl, reqBody, {
|
ws.http.fetch(payUrl, {
|
||||||
|
method: "POST",
|
||||||
|
body: reqBody,
|
||||||
timeout: getPayRequestTimeout(purchase),
|
timeout: getPayRequestTimeout(purchase),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -34,6 +34,7 @@ import {
|
|||||||
TalerProtocolTimestamp,
|
TalerProtocolTimestamp,
|
||||||
Transaction,
|
Transaction,
|
||||||
TransactionByIdRequest,
|
TransactionByIdRequest,
|
||||||
|
TransactionIdStr,
|
||||||
TransactionsRequest,
|
TransactionsRequest,
|
||||||
TransactionsResponse,
|
TransactionsResponse,
|
||||||
TransactionState,
|
TransactionState,
|
||||||
@ -1428,7 +1429,7 @@ export type ParsedTransactionIdentifier =
|
|||||||
|
|
||||||
export function constructTransactionIdentifier(
|
export function constructTransactionIdentifier(
|
||||||
pTxId: ParsedTransactionIdentifier,
|
pTxId: ParsedTransactionIdentifier,
|
||||||
): string {
|
): TransactionIdStr {
|
||||||
switch (pTxId.tag) {
|
switch (pTxId.tag) {
|
||||||
case TransactionType.Deposit:
|
case TransactionType.Deposit:
|
||||||
return `txn:${pTxId.tag}:${pTxId.depositGroupId}`;
|
return `txn:${pTxId.tag}:${pTxId.depositGroupId}`;
|
||||||
|
@ -1335,7 +1335,9 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
|
|||||||
return await prepareDepositGroup(ws, req);
|
return await prepareDepositGroup(ws, req);
|
||||||
}
|
}
|
||||||
case WalletApiOperation.GenerateDepositGroupTxId:
|
case WalletApiOperation.GenerateDepositGroupTxId:
|
||||||
return generateDepositGroupTxId();
|
return {
|
||||||
|
transactionId: generateDepositGroupTxId(),
|
||||||
|
};
|
||||||
case WalletApiOperation.CreateDepositGroup: {
|
case WalletApiOperation.CreateDepositGroup: {
|
||||||
const req = codecForCreateDepositGroupRequest().decode(payload);
|
const req = codecForCreateDepositGroupRequest().decode(payload);
|
||||||
return await createDepositGroup(ws, req);
|
return await createDepositGroup(ws, req);
|
||||||
|
Loading…
Reference in New Issue
Block a user