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