wallet-core: fix revocation, re-introduce reserves object store
This commit is contained in:
parent
70d0199572
commit
30e8fd83c2
@ -243,7 +243,7 @@ export async function runLibeufinBasicTest(t: GlobalTestState) {
|
||||
WalletApiOperation.AcceptManualWithdrawal,
|
||||
{
|
||||
exchangeBaseUrl: exchange.baseUrl,
|
||||
amount: "EUR:10",
|
||||
amount: "EUR:15",
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -17,12 +17,11 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { GlobalTestState, delayMs } from "../harness/harness.js";
|
||||
import { GlobalTestState } from "../harness/harness.js";
|
||||
import {
|
||||
SandboxUserBundle,
|
||||
NexusUserBundle,
|
||||
launchLibeufinServices,
|
||||
LibeufinSandboxApi,
|
||||
LibeufinNexusApi,
|
||||
} from "../harness/libeufin";
|
||||
|
||||
@ -73,7 +72,7 @@ export async function runLibeufinNexusBalanceTest(t: GlobalTestState) {
|
||||
user02sandbox.ebicsBankAccount.label, // debit
|
||||
user01sandbox.ebicsBankAccount.label, // credit
|
||||
"EUR:10",
|
||||
"first payment",
|
||||
"second payment",
|
||||
);
|
||||
|
||||
await LibeufinNexusApi.fetchTransactions(
|
||||
@ -82,13 +81,13 @@ export async function runLibeufinNexusBalanceTest(t: GlobalTestState) {
|
||||
"all", // range
|
||||
"report", // level
|
||||
);
|
||||
|
||||
|
||||
// Check that user 01 has 20, via Nexus.
|
||||
let accountInfo = await LibeufinNexusApi.getBankAccount(
|
||||
libeufinServices.libeufinNexus,
|
||||
user01nexus.localAccountName
|
||||
user01nexus.localAccountName,
|
||||
);
|
||||
t.assertTrue(accountInfo.data.lastSeenBalance == "EUR:20");
|
||||
t.assertAmountEquals(accountInfo.data.lastSeenBalance, "EUR:20");
|
||||
|
||||
// user 01 gives 30
|
||||
await libeufinServices.libeufinSandbox.makeTransaction(
|
||||
@ -107,8 +106,8 @@ export async function runLibeufinNexusBalanceTest(t: GlobalTestState) {
|
||||
|
||||
let accountInfoDebit = await LibeufinNexusApi.getBankAccount(
|
||||
libeufinServices.libeufinNexus,
|
||||
user01nexus.localAccountName
|
||||
user01nexus.localAccountName,
|
||||
);
|
||||
t.assertTrue(accountInfoDebit.data.lastSeenBalance == "-EUR:10");
|
||||
t.assertDeepEqual(accountInfoDebit.data.lastSeenBalance, "-EUR:10");
|
||||
}
|
||||
runLibeufinNexusBalanceTest.suites = ["libeufin"];
|
||||
|
@ -26,71 +26,71 @@ import {
|
||||
TestRunResult,
|
||||
} from "../harness/harness.js";
|
||||
import { runAgeRestrictionsTest } from "./test-age-restrictions.js";
|
||||
import { runBankApiTest } from "./test-bank-api";
|
||||
import { runClaimLoopTest } from "./test-claim-loop";
|
||||
import { runBankApiTest } from "./test-bank-api.js";
|
||||
import { runClaimLoopTest } from "./test-claim-loop.js";
|
||||
import { runClauseSchnorrTest } from "./test-clause-schnorr.js";
|
||||
import { runDenomUnofferedTest } from "./test-denom-unoffered.js";
|
||||
import { runDepositTest } from "./test-deposit";
|
||||
import { runExchangeManagementTest } from "./test-exchange-management";
|
||||
import { runDepositTest } from "./test-deposit.js";
|
||||
import { runExchangeManagementTest } from "./test-exchange-management.js";
|
||||
import { runExchangeTimetravelTest } from "./test-exchange-timetravel.js";
|
||||
import { runFeeRegressionTest } from "./test-fee-regression";
|
||||
import { runFeeRegressionTest } from "./test-fee-regression.js";
|
||||
import { runForcedSelectionTest } from "./test-forced-selection.js";
|
||||
import { runLibeufinApiBankaccountTest } from "./test-libeufin-api-bankaccount";
|
||||
import { runLibeufinApiBankconnectionTest } from "./test-libeufin-api-bankconnection";
|
||||
import { runLibeufinApiFacadeTest } from "./test-libeufin-api-facade";
|
||||
import { runLibeufinApiFacadeBadRequestTest } from "./test-libeufin-api-facade-bad-request";
|
||||
import { runLibeufinApiPermissionsTest } from "./test-libeufin-api-permissions";
|
||||
import { runLibeufinApiSandboxCamtTest } from "./test-libeufin-api-sandbox-camt";
|
||||
import { runLibeufinApiSandboxTransactionsTest } from "./test-libeufin-api-sandbox-transactions";
|
||||
import { runLibeufinApiSchedulingTest } from "./test-libeufin-api-scheduling";
|
||||
import { runLibeufinApiUsersTest } from "./test-libeufin-api-users";
|
||||
import { runLibeufinBadGatewayTest } from "./test-libeufin-bad-gateway";
|
||||
import { runLibeufinBasicTest } from "./test-libeufin-basic";
|
||||
import { runLibeufinC5xTest } from "./test-libeufin-c5x";
|
||||
import { runLibeufinAnastasisFacadeTest } from "./test-libeufin-facade-anastasis";
|
||||
import { runLibeufinKeyrotationTest } from "./test-libeufin-keyrotation";
|
||||
import { runLibeufinNexusBalanceTest } from "./test-libeufin-nexus-balance";
|
||||
import { runLibeufinRefundTest } from "./test-libeufin-refund";
|
||||
import { runLibeufinRefundMultipleUsersTest } from "./test-libeufin-refund-multiple-users";
|
||||
import { runLibeufinSandboxWireTransferCliTest } from "./test-libeufin-sandbox-wire-transfer-cli";
|
||||
import { runLibeufinTutorialTest } from "./test-libeufin-tutorial";
|
||||
import { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion";
|
||||
import { runMerchantInstancesTest } from "./test-merchant-instances";
|
||||
import { runLibeufinApiBankaccountTest } from "./test-libeufin-api-bankaccount.js";
|
||||
import { runLibeufinApiBankconnectionTest } from "./test-libeufin-api-bankconnection.js";
|
||||
import { runLibeufinApiFacadeTest } from "./test-libeufin-api-facade.js";
|
||||
import { runLibeufinApiFacadeBadRequestTest } from "./test-libeufin-api-facade-bad-request.js";
|
||||
import { runLibeufinApiPermissionsTest } from "./test-libeufin-api-permissions.js";
|
||||
import { runLibeufinApiSandboxCamtTest } from "./test-libeufin-api-sandbox-camt.js";
|
||||
import { runLibeufinApiSandboxTransactionsTest } from "./test-libeufin-api-sandbox-transactions.js";
|
||||
import { runLibeufinApiSchedulingTest } from "./test-libeufin-api-scheduling.js";
|
||||
import { runLibeufinApiUsersTest } from "./test-libeufin-api-users.js";
|
||||
import { runLibeufinBadGatewayTest } from "./test-libeufin-bad-gateway.js";
|
||||
import { runLibeufinBasicTest } from "./test-libeufin-basic.js";
|
||||
import { runLibeufinC5xTest } from "./test-libeufin-c5x.js";
|
||||
import { runLibeufinAnastasisFacadeTest } from "./test-libeufin-facade-anastasis.js";
|
||||
import { runLibeufinKeyrotationTest } from "./test-libeufin-keyrotation.js";
|
||||
import { runLibeufinNexusBalanceTest } from "./test-libeufin-nexus-balance.js";
|
||||
import { runLibeufinRefundTest } from "./test-libeufin-refund.js";
|
||||
import { runLibeufinRefundMultipleUsersTest } from "./test-libeufin-refund-multiple-users.js";
|
||||
import { runLibeufinSandboxWireTransferCliTest } from "./test-libeufin-sandbox-wire-transfer-cli.js";
|
||||
import { runLibeufinTutorialTest } from "./test-libeufin-tutorial.js";
|
||||
import { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion.js";
|
||||
import { runMerchantInstancesTest } from "./test-merchant-instances.js";
|
||||
import { runMerchantInstancesDeleteTest } from "./test-merchant-instances-delete";
|
||||
import { runMerchantInstancesUrlsTest } from "./test-merchant-instances-urls";
|
||||
import { runMerchantLongpollingTest } from "./test-merchant-longpolling";
|
||||
import { runMerchantRefundApiTest } from "./test-merchant-refund-api";
|
||||
import { runMerchantInstancesUrlsTest } from "./test-merchant-instances-urls.js";
|
||||
import { runMerchantLongpollingTest } from "./test-merchant-longpolling.js";
|
||||
import { runMerchantRefundApiTest } from "./test-merchant-refund-api.js";
|
||||
import { runMerchantSpecPublicOrdersTest } from "./test-merchant-spec-public-orders.js";
|
||||
import { runPayAbortTest } from "./test-pay-abort";
|
||||
import { runPayPaidTest } from "./test-pay-paid";
|
||||
import { runPaymentTest } from "./test-payment";
|
||||
import { runPaymentClaimTest } from "./test-payment-claim";
|
||||
import { runPaymentFaultTest } from "./test-payment-fault";
|
||||
import { runPayAbortTest } from "./test-pay-abort.js";
|
||||
import { runPayPaidTest } from "./test-pay-paid.js";
|
||||
import { runPaymentTest } from "./test-payment.js";
|
||||
import { runPaymentClaimTest } from "./test-payment-claim.js";
|
||||
import { runPaymentFaultTest } from "./test-payment-fault.js";
|
||||
import { runPaymentForgettableTest } from "./test-payment-forgettable.js";
|
||||
import { runPaymentIdempotencyTest } from "./test-payment-idempotency";
|
||||
import { runPaymentMultipleTest } from "./test-payment-multiple";
|
||||
import { runPaymentDemoTest } from "./test-payment-on-demo";
|
||||
import { runPaymentTransientTest } from "./test-payment-transient";
|
||||
import { runPaymentIdempotencyTest } from "./test-payment-idempotency.js";
|
||||
import { runPaymentMultipleTest } from "./test-payment-multiple.js";
|
||||
import { runPaymentDemoTest } from "./test-payment-on-demo.js";
|
||||
import { runPaymentTransientTest } from "./test-payment-transient.js";
|
||||
import { runPaymentZeroTest } from "./test-payment-zero.js";
|
||||
import { runPaywallFlowTest } from "./test-paywall-flow";
|
||||
import { runPaywallFlowTest } from "./test-paywall-flow.js";
|
||||
import { runPeerToPeerPullTest } from "./test-peer-to-peer-pull.js";
|
||||
import { runPeerToPeerPushTest } from "./test-peer-to-peer-push.js";
|
||||
import { runRefundTest } from "./test-refund";
|
||||
import { runRefundAutoTest } from "./test-refund-auto";
|
||||
import { runRefundGoneTest } from "./test-refund-gone";
|
||||
import { runRefundIncrementalTest } from "./test-refund-incremental";
|
||||
import { runRevocationTest } from "./test-revocation";
|
||||
import { runTimetravelAutorefreshTest } from "./test-timetravel-autorefresh";
|
||||
import { runTimetravelWithdrawTest } from "./test-timetravel-withdraw";
|
||||
import { runTippingTest } from "./test-tipping";
|
||||
import { runWalletBackupBasicTest } from "./test-wallet-backup-basic";
|
||||
import { runWalletBackupDoublespendTest } from "./test-wallet-backup-doublespend";
|
||||
import { runRefundTest } from "./test-refund.js";
|
||||
import { runRefundAutoTest } from "./test-refund-auto.js";
|
||||
import { runRefundGoneTest } from "./test-refund-gone.js";
|
||||
import { runRefundIncrementalTest } from "./test-refund-incremental.js";
|
||||
import { runRevocationTest } from "./test-revocation.js";
|
||||
import { runTimetravelAutorefreshTest } from "./test-timetravel-autorefresh.js";
|
||||
import { runTimetravelWithdrawTest } from "./test-timetravel-withdraw.js";
|
||||
import { runTippingTest } from "./test-tipping.js";
|
||||
import { runWalletBackupBasicTest } from "./test-wallet-backup-basic.js";
|
||||
import { runWalletBackupDoublespendTest } from "./test-wallet-backup-doublespend.js";
|
||||
import { runWalletDblessTest } from "./test-wallet-dbless.js";
|
||||
import { runWallettestingTest } from "./test-wallettesting";
|
||||
import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank";
|
||||
import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated";
|
||||
import { runWallettestingTest } from "./test-wallettesting.js";
|
||||
import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank.js";
|
||||
import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated.js";
|
||||
import { runWithdrawalFakebankTest } from "./test-withdrawal-fakebank.js";
|
||||
import { runTestWithdrawalManualTest } from "./test-withdrawal-manual";
|
||||
import { runTestWithdrawalManualTest } from "./test-withdrawal-manual.js";
|
||||
|
||||
/**
|
||||
* Test runner.
|
||||
|
@ -1224,6 +1224,7 @@ export const enum WithdrawalRecordType {
|
||||
BankIntegrated = "bank-integrated",
|
||||
PeerPullCredit = "peer-pull-credit",
|
||||
PeerPushCredit = "peer-push-credit",
|
||||
Recoup = "recoup",
|
||||
}
|
||||
|
||||
export interface WgInfoBankIntegrated {
|
||||
@ -1253,11 +1254,16 @@ export interface WgInfoBankPeerPush {
|
||||
withdrawalType: WithdrawalRecordType.PeerPushCredit;
|
||||
}
|
||||
|
||||
export interface WgInfoBankRecoup {
|
||||
withdrawalType: WithdrawalRecordType.Recoup;
|
||||
}
|
||||
|
||||
export type WgInfo =
|
||||
| WgInfoBankIntegrated
|
||||
| WgInfoBankManual
|
||||
| WgInfoBankPeerPull
|
||||
| WgInfoBankPeerPush;
|
||||
| WgInfoBankPeerPush
|
||||
| WgInfoBankRecoup;
|
||||
|
||||
/**
|
||||
* Group of withdrawal operations that need to be executed.
|
||||
@ -1287,6 +1293,8 @@ export interface WithdrawalGroupRecord {
|
||||
|
||||
/**
|
||||
* The reserve private key.
|
||||
*
|
||||
* FIXME: Already in the reserves object store, redundant!
|
||||
*/
|
||||
reservePriv: string;
|
||||
|
||||
@ -1355,9 +1363,9 @@ export interface WithdrawalGroupRecord {
|
||||
denomSelUid: string;
|
||||
|
||||
/**
|
||||
* Retry info, always present even on completed operations so that indexing works.
|
||||
* Retry info.
|
||||
*/
|
||||
retryInfo: RetryInfo;
|
||||
retryInfo?: RetryInfo;
|
||||
|
||||
lastError: TalerErrorDetail | undefined;
|
||||
}
|
||||
@ -1386,6 +1394,8 @@ export interface RecoupGroupRecord {
|
||||
*/
|
||||
recoupGroupId: string;
|
||||
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
timestampStarted: TalerProtocolTimestamp;
|
||||
|
||||
timestampFinished: TalerProtocolTimestamp | undefined;
|
||||
@ -1724,6 +1734,13 @@ export interface PeerPullPaymentIncomingRecord {
|
||||
contractPriv: string;
|
||||
}
|
||||
|
||||
// FIXME: give this some smaller "row ID" to
|
||||
// reference in other records?
|
||||
export interface ReserveRecord {
|
||||
reservePub: string;
|
||||
reservePriv: string;
|
||||
}
|
||||
|
||||
export const WalletStoresV1 = {
|
||||
coins: describeStore(
|
||||
describeContents<CoinRecord>("coins", {
|
||||
@ -1735,6 +1752,12 @@ export const WalletStoresV1 = {
|
||||
byCoinEvHash: describeIndex("byCoinEvHash", "coinEvHash"),
|
||||
},
|
||||
),
|
||||
reserves: describeStore(
|
||||
describeContents<ReserveRecord>("reserves", {
|
||||
keyPath: "reservePub",
|
||||
}),
|
||||
{},
|
||||
),
|
||||
config: describeStore(
|
||||
describeContents<ConfigRecord>("config", { keyPath: "key" }),
|
||||
{},
|
||||
|
@ -73,7 +73,6 @@ export interface MerchantOperations {
|
||||
): Promise<MerchantInfo>;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface for exchange-related operations.
|
||||
*/
|
||||
@ -113,6 +112,7 @@ export interface RecoupOperations {
|
||||
refreshGroups: typeof WalletStoresV1.refreshGroups;
|
||||
coins: typeof WalletStoresV1.coins;
|
||||
}>,
|
||||
exchangeBaseUrl: string,
|
||||
coinPubs: string[],
|
||||
): Promise<string>;
|
||||
processRecoupGroup(
|
||||
|
@ -743,6 +743,7 @@ async function updateExchangeFromUrlImpl(
|
||||
recoupGroupId = await ws.recoupOps.createRecoupGroup(
|
||||
ws,
|
||||
tx,
|
||||
exchange.baseUrl,
|
||||
newlyRevokedCoinPubs,
|
||||
);
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ async function gatherWithdrawalPending(
|
||||
resp.pendingOperations.push({
|
||||
type: PendingTaskType.Withdraw,
|
||||
givesLifeness: true,
|
||||
timestampDue: wsr.retryInfo.nextRetry,
|
||||
timestampDue: wsr.retryInfo?.nextRetry ?? AbsoluteTime.now(),
|
||||
withdrawalGroupId: wsr.withdrawalGroupId,
|
||||
lastError: wsr.lastError,
|
||||
retryInfo: wsr.retryInfo,
|
||||
|
@ -36,16 +36,17 @@ import {
|
||||
TalerErrorDetail,
|
||||
TalerProtocolTimestamp,
|
||||
URL,
|
||||
codecForReserveStatus,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import {
|
||||
CoinRecord,
|
||||
CoinSourceType,
|
||||
CoinStatus,
|
||||
OperationStatus,
|
||||
RecoupGroupRecord,
|
||||
RefreshCoinSource,
|
||||
ReserveRecordStatus,
|
||||
WalletStoresV1,
|
||||
WithdrawalRecordType,
|
||||
WithdrawCoinSource,
|
||||
} from "../db.js";
|
||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||
@ -109,6 +110,10 @@ async function reportRecoupError(
|
||||
ws.notify({ type: NotificationType.RecoupOperationError, error: err });
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a recoup group record in the database after marking
|
||||
* a coin in the group as finished.
|
||||
*/
|
||||
async function putGroupAsFinished(
|
||||
ws: InternalWalletState,
|
||||
tx: GetReadWriteAccess<{
|
||||
@ -127,29 +132,6 @@ async function putGroupAsFinished(
|
||||
return;
|
||||
}
|
||||
recoupGroup.recoupFinishedPerCoin[coinIdx] = true;
|
||||
let allFinished = true;
|
||||
for (const b of recoupGroup.recoupFinishedPerCoin) {
|
||||
if (!b) {
|
||||
allFinished = false;
|
||||
}
|
||||
}
|
||||
if (allFinished) {
|
||||
logger.info("all recoups of recoup group are finished");
|
||||
recoupGroup.timestampFinished = TalerProtocolTimestamp.now();
|
||||
recoupGroup.retryInfo = RetryInfo.reset();
|
||||
recoupGroup.lastError = undefined;
|
||||
if (recoupGroup.scheduleRefreshCoins.length > 0) {
|
||||
const refreshGroupId = await createRefreshGroup(
|
||||
ws,
|
||||
tx,
|
||||
recoupGroup.scheduleRefreshCoins.map((x) => ({ coinPub: x })),
|
||||
RefreshReason.Recoup,
|
||||
);
|
||||
processRefreshGroup(ws, refreshGroupId.refreshGroupId).catch((e) => {
|
||||
logger.error(`error while refreshing after recoup ${e}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
await tx.recoupGroups.put(recoupGroup);
|
||||
}
|
||||
|
||||
@ -258,8 +240,6 @@ async function recoupWithdrawCoin(
|
||||
const currency = updatedCoin.currentAmount.currency;
|
||||
updatedCoin.currentAmount = Amounts.getZero(currency);
|
||||
await tx.coins.put(updatedCoin);
|
||||
// FIXME: Actually withdraw here!
|
||||
// await internalCreateWithdrawalGroup(ws, {...});
|
||||
await putGroupAsFinished(ws, tx, recoupGroup, coinIdx);
|
||||
});
|
||||
|
||||
@ -392,7 +372,7 @@ async function processRecoupGroupImpl(
|
||||
): Promise<void> {
|
||||
const forceNow = options.forceNow ?? false;
|
||||
await setupRecoupRetry(ws, recoupGroupId, { reset: forceNow });
|
||||
const recoupGroup = await ws.db
|
||||
let recoupGroup = await ws.db
|
||||
.mktx((x) => ({
|
||||
recoupGroups: x.recoupGroups,
|
||||
}))
|
||||
@ -416,23 +396,105 @@ async function processRecoupGroupImpl(
|
||||
});
|
||||
await Promise.all(ps);
|
||||
|
||||
const reserveSet = new Set<string>();
|
||||
for (let i = 0; i < recoupGroup.coinPubs.length; i++) {
|
||||
const coinPub = recoupGroup.coinPubs[i];
|
||||
const coin = await ws.db
|
||||
.mktx((x) => ({
|
||||
coins: x.coins,
|
||||
}))
|
||||
.runReadOnly(async (tx) => {
|
||||
return tx.coins.get(coinPub);
|
||||
});
|
||||
if (!coin) {
|
||||
throw Error(`Coin ${coinPub} not found, can't request recoup`);
|
||||
}
|
||||
if (coin.coinSource.type === CoinSourceType.Withdraw) {
|
||||
reserveSet.add(coin.coinSource.reservePub);
|
||||
recoupGroup = await ws.db
|
||||
.mktx((x) => ({
|
||||
recoupGroups: x.recoupGroups,
|
||||
}))
|
||||
.runReadOnly(async (tx) => {
|
||||
return tx.recoupGroups.get(recoupGroupId);
|
||||
});
|
||||
if (!recoupGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const b of recoupGroup.recoupFinishedPerCoin) {
|
||||
if (!b) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("all recoups of recoup group are finished");
|
||||
|
||||
const reserveSet = new Set<string>();
|
||||
const reservePrivMap: Record<string, string> = {};
|
||||
for (let i = 0; i < recoupGroup.coinPubs.length; i++) {
|
||||
const coinPub = recoupGroup.coinPubs[i];
|
||||
await ws.db
|
||||
.mktx((x) => ({
|
||||
coins: x.coins,
|
||||
reserves: x.reserves,
|
||||
}))
|
||||
.runReadOnly(async (tx) => {
|
||||
const coin = await tx.coins.get(coinPub);
|
||||
if (!coin) {
|
||||
throw Error(`Coin ${coinPub} not found, can't request recoup`);
|
||||
}
|
||||
if (coin.coinSource.type === CoinSourceType.Withdraw) {
|
||||
const reserve = await tx.reserves.get(coin.coinSource.reservePub);
|
||||
if (!reserve) {
|
||||
return;
|
||||
}
|
||||
reserveSet.add(coin.coinSource.reservePub);
|
||||
reservePrivMap[coin.coinSource.reservePub] = reserve.reservePriv;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (const reservePub of reserveSet) {
|
||||
const reserveUrl = new URL(
|
||||
`reserves/${reservePub}`,
|
||||
recoupGroup.exchangeBaseUrl,
|
||||
);
|
||||
logger.info(`querying reserve status for recoup via ${reserveUrl}`);
|
||||
|
||||
const resp = await ws.http.get(reserveUrl.href);
|
||||
|
||||
const result = await readSuccessResponseJsonOrThrow(
|
||||
resp,
|
||||
codecForReserveStatus(),
|
||||
);
|
||||
await internalCreateWithdrawalGroup(ws, {
|
||||
amount: Amounts.parseOrThrow(result.balance),
|
||||
exchangeBaseUrl: recoupGroup.exchangeBaseUrl,
|
||||
reserveStatus: ReserveRecordStatus.QueryingStatus,
|
||||
reserveKeyPair: {
|
||||
pub: reservePub,
|
||||
priv: reservePrivMap[reservePub],
|
||||
},
|
||||
wgInfo: {
|
||||
withdrawalType: WithdrawalRecordType.Recoup,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await ws.db
|
||||
.mktx((x) => ({
|
||||
recoupGroups: x.recoupGroups,
|
||||
denominations: WalletStoresV1.denominations,
|
||||
refreshGroups: WalletStoresV1.refreshGroups,
|
||||
coins: WalletStoresV1.coins,
|
||||
}))
|
||||
.runReadWrite(async (tx) => {
|
||||
const rg2 = await tx.recoupGroups.get(recoupGroupId);
|
||||
if (!rg2) {
|
||||
return;
|
||||
}
|
||||
rg2.timestampFinished = TalerProtocolTimestamp.now();
|
||||
rg2.retryInfo = RetryInfo.reset();
|
||||
rg2.lastError = undefined;
|
||||
if (rg2.scheduleRefreshCoins.length > 0) {
|
||||
const refreshGroupId = await createRefreshGroup(
|
||||
ws,
|
||||
tx,
|
||||
rg2.scheduleRefreshCoins.map((x) => ({ coinPub: x })),
|
||||
RefreshReason.Recoup,
|
||||
);
|
||||
processRefreshGroup(ws, refreshGroupId.refreshGroupId).catch((e) => {
|
||||
logger.error(`error while refreshing after recoup ${e}`);
|
||||
});
|
||||
}
|
||||
await tx.recoupGroups.put(rg2);
|
||||
});
|
||||
}
|
||||
|
||||
export async function createRecoupGroup(
|
||||
@ -443,12 +505,14 @@ export async function createRecoupGroup(
|
||||
refreshGroups: typeof WalletStoresV1.refreshGroups;
|
||||
coins: typeof WalletStoresV1.coins;
|
||||
}>,
|
||||
exchangeBaseUrl: string,
|
||||
coinPubs: string[],
|
||||
): Promise<string> {
|
||||
const recoupGroupId = encodeCrock(getRandomBytes(32));
|
||||
|
||||
const recoupGroup: RecoupGroupRecord = {
|
||||
recoupGroupId,
|
||||
exchangeBaseUrl: exchangeBaseUrl,
|
||||
coinPubs: coinPubs,
|
||||
lastError: undefined,
|
||||
timestampFinished: undefined,
|
||||
|
@ -1135,6 +1135,22 @@ async function processWithdrawGroupImpl(
|
||||
withdrawalGroup.exchangeBaseUrl,
|
||||
);
|
||||
|
||||
if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) {
|
||||
await ws.db
|
||||
.mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
|
||||
.runReadWrite(async (tx) => {
|
||||
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
if (!wg) {
|
||||
return;
|
||||
}
|
||||
wg.operationStatus = OperationStatus.Finished;
|
||||
delete wg.lastError;
|
||||
delete wg.retryInfo;
|
||||
await tx.withdrawalGroups.put(wg);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const numTotalCoins = withdrawalGroup.denomsSel.selectedDenoms
|
||||
.map((x) => x.count)
|
||||
.reduce((a, b) => a + b);
|
||||
@ -1709,7 +1725,6 @@ export async function internalCreateWithdrawalGroup(
|
||||
args: {
|
||||
reserveStatus: ReserveRecordStatus;
|
||||
amount: AmountJson;
|
||||
bankInfo?: ReserveBankInfo;
|
||||
exchangeBaseUrl: string;
|
||||
forcedDenomSel?: ForcedDenomSel;
|
||||
reserveKeyPair?: EddsaKeypair;
|
||||
@ -1776,12 +1791,17 @@ export async function internalCreateWithdrawalGroup(
|
||||
await ws.db
|
||||
.mktx((x) => ({
|
||||
withdrawalGroups: x.withdrawalGroups,
|
||||
reserves: x.reserves,
|
||||
exchanges: x.exchanges,
|
||||
exchangeDetails: x.exchangeDetails,
|
||||
exchangeTrust: x.exchangeTrust,
|
||||
}))
|
||||
.runReadWrite(async (tx) => {
|
||||
await tx.withdrawalGroups.add(withdrawalGroup);
|
||||
await tx.reserves.put({
|
||||
reservePub: withdrawalGroup.reservePub,
|
||||
reservePriv: withdrawalGroup.reservePriv,
|
||||
});
|
||||
|
||||
if (!isAudited && !isTrusted) {
|
||||
await tx.exchangeTrust.put({
|
||||
@ -1906,7 +1926,6 @@ export async function createManualWithdrawal(
|
||||
withdrawalType: WithdrawalRecordType.BankManual,
|
||||
},
|
||||
exchangeBaseUrl: req.exchangeBaseUrl,
|
||||
bankInfo: undefined,
|
||||
forcedDenomSel: req.forcedDenomSel,
|
||||
restrictAge: req.restrictAge,
|
||||
reserveStatus: ReserveRecordStatus.QueryingStatus,
|
||||
|
@ -182,7 +182,7 @@ export interface PendingRecoupTask {
|
||||
export interface PendingWithdrawTask {
|
||||
type: PendingTaskType.Withdraw;
|
||||
lastError: TalerErrorDetail | undefined;
|
||||
retryInfo: RetryInfo;
|
||||
retryInfo?: RetryInfo;
|
||||
withdrawalGroupId: string;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user