diff options
11 files changed, 140 insertions, 83 deletions
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts index 15ca1ea95..fd2d48398 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts @@ -243,7 +243,9 @@ export interface TalerCryptoInterface { signRefund(req: SignRefundRequest): Promise<SignRefundResponse>; - signDeletePurse(req: SignDeletePurseRequest): Promise<SignDeletePurseResponse>; + signDeletePurse( + req: SignDeletePurseRequest, + ): Promise<SignDeletePurseResponse>; } /** @@ -1695,7 +1697,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = { }); return { sig: sigResp.sig, - } + }; }, }; diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts index 7eaa4a55a..2204fac71 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts @@ -271,7 +271,6 @@ export interface SignRefundResponse { sig: string; } - export interface SignDeletePurseRequest { pursePriv: string; } diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts index 68cce2b8d..4dfdff3f7 100644 --- a/packages/taler-wallet-core/src/dbless.ts +++ b/packages/taler-wallet-core/src/dbless.ts @@ -120,13 +120,7 @@ export interface TopupReserveWithDemobankArgs { export async function topupReserveWithDemobank( args: TopupReserveWithDemobankArgs, ) { - const { - http, - bankAccessApiBaseUrl, - amount, - exchangeInfo, - reservePub, - } = args; + const { http, bankAccessApiBaseUrl, amount, exchangeInfo, reservePub } = args; const bankHandle: BankServiceHandle = { bankAccessApiBaseUrl: bankAccessApiBaseUrl, http, diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index d3346a3a2..cda5a012b 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -62,11 +62,7 @@ import { InternalWalletState } from "../../internal-wallet-state.js"; import { assertUnreachable } from "../../util/assertUnreachable.js"; import { checkLogicInvariant } from "../../util/invariants.js"; import { GetReadOnlyAccess, GetReadWriteAccess } from "../../util/query.js"; -import { - makeCoinAvailable, - makeTombstoneId, - TombstoneTag, -} from "../common.js"; +import { makeCoinAvailable, makeTombstoneId, TombstoneTag } from "../common.js"; import { getExchangeDetails } from "../exchanges.js"; import { extractContractData } from "../pay-merchant.js"; import { provideBackupState } from "./state.js"; diff --git a/packages/taler-wallet-core/src/operations/common.ts b/packages/taler-wallet-core/src/operations/common.ts index ef5c54002..a64f78b03 100644 --- a/packages/taler-wallet-core/src/operations/common.ts +++ b/packages/taler-wallet-core/src/operations/common.ts @@ -464,7 +464,7 @@ export type ParsedTombstone = tag: TombstoneTag.DeleteWithdrawalGroup; withdrawalGroupId: string; } - | { tag: TombstoneTag.DeleteRefund; refundGroupId: string } + | { tag: TombstoneTag.DeleteRefund; refundGroupId: string }; export function constructTombstone(p: ParsedTombstone): TombstoneIdStr { switch (p.tag) { diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index 6387fc9b7..64180a3ea 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -495,8 +495,7 @@ async function waitForRefreshOnDepositGroup( if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) { newOpState = DepositOperationStatus.Aborted; } else if ( - refreshGroup.operationStatus === - RefreshOperationStatus.Failed + refreshGroup.operationStatus === RefreshOperationStatus.Failed ) { newOpState = DepositOperationStatus.Aborted; } diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts index fc2718ce5..447ffce8f 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts @@ -208,14 +208,13 @@ async function longpollKycStatus( const transitionInfo = await ws.db .mktx((x) => [x.peerPullPaymentInitiations]) .runReadWrite(async (tx) => { - const peerIni = await tx.peerPullPaymentInitiations.get( - pursePub, - ); + const peerIni = await tx.peerPullPaymentInitiations.get(pursePub); if (!peerIni) { return; } if ( - peerIni.status !== PeerPullPaymentInitiationStatus.PendingMergeKycRequired + peerIni.status !== + PeerPullPaymentInitiationStatus.PendingMergeKycRequired ) { return; } @@ -254,10 +253,7 @@ async function processPeerPullCreditAbortingDeletePurse( const sigResp = await ws.cryptoApi.signDeletePurse({ pursePriv, }); - const purseUrl = new URL( - `purses/${pursePub}`, - peerPullIni.exchangeBaseUrl, - ); + const purseUrl = new URL(`purses/${pursePub}`, peerPullIni.exchangeBaseUrl); const resp = await ws.http.fetch(purseUrl.href, { method: "DELETE", headers: { @@ -517,9 +513,7 @@ async function processPeerPullCreditKycRequired( const { transitionInfo, result } = await ws.db .mktx((x) => [x.peerPullPaymentInitiations]) .runReadWrite(async (tx) => { - const peerInc = await tx.peerPullPaymentInitiations.get( - pursePub, - ); + const peerInc = await tx.peerPullPaymentInitiations.get(pursePub); if (!peerInc) { return { transitionInfo: undefined, @@ -532,7 +526,8 @@ async function processPeerPullCreditKycRequired( requirementRow: kycPending.requirement_row, }; peerInc.kycUrl = kycStatus.kyc_url; - peerInc.status = PeerPullPaymentInitiationStatus.PendingMergeKycRequired; + peerInc.status = + PeerPullPaymentInitiationStatus.PendingMergeKycRequired; const newTxState = computePeerPullCreditTransactionState(peerInc); await tx.peerPullPaymentInitiations.put(peerInc); // We'll remove this eventually! New clients should rely on the diff --git a/packages/taler-wallet-core/src/operations/testing.ts b/packages/taler-wallet-core/src/operations/testing.ts index 238a5dc66..ece71439c 100644 --- a/packages/taler-wallet-core/src/operations/testing.ts +++ b/packages/taler-wallet-core/src/operations/testing.ts @@ -45,14 +45,24 @@ import { PreparePayResultType, } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../internal-wallet-state.js"; -import { confirmPay, preparePayForUri, startRefundQueryForUri } from "./pay-merchant.js"; +import { + confirmPay, + preparePayForUri, + startRefundQueryForUri, +} from "./pay-merchant.js"; import { getBalances } from "./balance.js"; import { checkLogicInvariant } from "../util/invariants.js"; import { acceptWithdrawalFromUri } from "./withdraw.js"; import { updateExchangeFromUrl } from "./exchanges.js"; import { initiatePeerPullPayment } from "./pay-peer-pull-credit.js"; -import { preparePeerPullDebit, confirmPeerPullDebit } from "./pay-peer-pull-debit.js"; -import { preparePeerPushCredit, confirmPeerPushCredit } from "./pay-peer-push-credit.js"; +import { + preparePeerPullDebit, + confirmPeerPullDebit, +} from "./pay-peer-pull-debit.js"; +import { + preparePeerPushCredit, + confirmPeerPushCredit, +} from "./pay-peer-push-credit.js"; import { initiatePeerPushDebit } from "./pay-peer-push-debit.js"; const logger = new Logger("operations/testing.ts"); diff --git a/packages/taler-wallet-core/src/util/coinSelection.test.ts b/packages/taler-wallet-core/src/util/coinSelection.test.ts index 7f4164aa9..448982c7f 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.test.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.test.ts @@ -15,6 +15,8 @@ */ import test, { ExecutionContext } from "ava"; import { + AmountMode, + OperationType, calculatePlanFormAvailableCoins, selectCoinForOperation, } from "./coinSelection.js"; @@ -24,6 +26,7 @@ import { AmountJson, Amounts, Duration, + TransactionAmountMode, TransactionType, } from "@gnu-taler/taler-util"; @@ -67,10 +70,15 @@ test("get effective 2", (t) => { [kudos(2), 5], [kudos(5), 5], ]; - const result = selectCoinForOperation("credit", kudos(2), "net", { - list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), - exchanges: {}, - }); + const result = selectCoinForOperation( + OperationType.Credit, + kudos(2), + AmountMode.Net, + { + list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), + exchanges: {}, + }, + ); expect(t, result.coins).deep.equal(["KUDOS:2"]); t.assert(result.refresh === undefined); }); @@ -80,10 +88,15 @@ test("get raw 4", (t) => { [kudos(2), 5], [kudos(5), 5], ]; - const result = selectCoinForOperation("credit", kudos(4), "gross", { - list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), - exchanges: {}, - }); + const result = selectCoinForOperation( + OperationType.Credit, + kudos(4), + AmountMode.Gross, + { + list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), + exchanges: {}, + }, + ); expect(t, result.coins).deep.equal(["KUDOS:2", "KUDOS:2"]); t.assert(result.refresh === undefined); @@ -94,10 +107,15 @@ test("send effective 6", (t) => { [kudos(2), 5], [kudos(5), 5], ]; - const result = selectCoinForOperation("debit", kudos(6), "gross", { - list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), - exchanges: {}, - }); + const result = selectCoinForOperation( + OperationType.Debit, + kudos(6), + AmountMode.Gross, + { + list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), + exchanges: {}, + }, + ); expect(t, result.coins).deep.equal(["KUDOS:5"]); t.assert(result.refresh?.selected === "KUDOS:2"); @@ -108,10 +126,15 @@ test("send raw 6", (t) => { [kudos(2), 5], [kudos(5), 5], ]; - const result = selectCoinForOperation("debit", kudos(6), "gross", { - list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), - exchanges: {}, - }); + const result = selectCoinForOperation( + OperationType.Debit, + kudos(6), + AmountMode.Gross, + { + list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), + exchanges: {}, + }, + ); expect(t, result.coins).deep.equal(["KUDOS:5"]); t.assert(result.refresh?.selected === "KUDOS:2"); @@ -122,10 +145,15 @@ test("send raw 20 (not enough)", (t) => { [kudos(2), 1], [kudos(5), 2], ]; - const result = selectCoinForOperation("debit", kudos(20), "gross", { - list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), - exchanges: {}, - }); + const result = selectCoinForOperation( + OperationType.Debit, + kudos(20), + AmountMode.Gross, + { + list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), + exchanges: {}, + }, + ); expect(t, result.coins).deep.equal(["KUDOS:5", "KUDOS:5", "KUDOS:2"]); t.assert(result.refresh === undefined); @@ -147,7 +175,7 @@ test("deposit effective 2 ", (t) => { const result = calculatePlanFormAvailableCoins( TransactionType.Deposit, kudos(2), - "effective", + TransactionAmountMode.Effective, { list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), exchanges: { @@ -173,7 +201,7 @@ test("deposit raw 2 ", (t) => { const result = calculatePlanFormAvailableCoins( TransactionType.Deposit, kudos(2), - "raw", + TransactionAmountMode.Raw, { list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), exchanges: { @@ -199,7 +227,7 @@ test("withdraw raw 21 ", (t) => { const result = calculatePlanFormAvailableCoins( TransactionType.Withdrawal, kudos(21), - "raw", + TransactionAmountMode.Raw, { list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), exchanges: { diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index f6d8abcd4..c5a810c4f 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -46,6 +46,7 @@ import { PayCoinSelection, PayMerchantInsufficientBalanceDetails, strcmp, + TransactionAmountMode, TransactionType, } from "@gnu-taler/taler-util"; import { @@ -818,7 +819,7 @@ function getCoinsFilter(req: GetPlanForOperationRequest): CoinsFilter { export function calculatePlanFormAvailableCoins( transactionType: TransactionType, amount: AmountJson, - mode: "effective" | "raw", + mode: TransactionAmountMode, availableCoins: AvailableCoins, ) { const operationType = getOperationType(transactionType); @@ -828,7 +829,9 @@ export function calculatePlanFormAvailableCoins( usableCoins = selectCoinForOperation( operationType, amount, - mode === "effective" ? "net" : "gross", + mode === TransactionAmountMode.Effective + ? AmountMode.Net + : AmountMode.Gross, availableCoins, ); break; @@ -839,11 +842,11 @@ export function calculatePlanFormAvailableCoins( //are from that exchange const wireFee = Object.values(availableCoins.exchanges)[0].wireFee!; - if (mode === "effective") { + if (mode === TransactionAmountMode.Effective) { usableCoins = selectCoinForOperation( operationType, amount, - "gross", + AmountMode.Gross, availableCoins, ); @@ -857,7 +860,7 @@ export function calculatePlanFormAvailableCoins( usableCoins = selectCoinForOperation( operationType, adjustedAmount, - "net", + AmountMode.Net, availableCoins, ); @@ -914,6 +917,27 @@ export async function getPlanForOperation( } /** + * If the operation going to be plan subtracts + * or adds amount in the wallet db + */ +export enum OperationType { + Credit = "credit", + Debit = "debit", +} + +/** + * How the amount should be interpreted + * net = without fee + * gross = with fee + * + * Net value is always lower than gross + */ +export enum AmountMode { + Net = "net", + Gross = "gross", +} + +/** * * @param op defined which fee are we taking into consideration: deposits or withdraw * @param limit the total amount limit of the operation @@ -922,9 +946,9 @@ export async function getPlanForOperation( * @returns */ export function selectCoinForOperation( - op: "debit" | "credit", + op: OperationType, limit: AmountJson, - mode: "net" | "gross", + mode: AmountMode, coins: AvailableCoins, ): SelectedCoins { const result: SelectedCoins = { @@ -951,8 +975,11 @@ export function selectCoinForOperation( iterateDenoms: while (denomIdx < coins.list.length) { const denom = coins.list[denomIdx]; let total = - op === "credit" ? Number.MAX_SAFE_INTEGER : denom.totalAvailable ?? 0; - const opFee = op === "credit" ? denom.denomWithdraw : denom.denomDeposit; + op === OperationType.Credit + ? Number.MAX_SAFE_INTEGER + : denom.totalAvailable ?? 0; + const opFee = + op === OperationType.Credit ? denom.denomWithdraw : denom.denomDeposit; const contribution = Amounts.sub(denom.value, opFee).amount; if (Amounts.isZero(contribution)) { @@ -969,7 +996,7 @@ export function selectCoinForOperation( contribution, ).amount; - const progress = mode === "gross" ? nextValue : nextContribution; + const progress = mode === AmountMode.Gross ? nextValue : nextContribution; if (Amounts.cmp(progress, limit) === 1) { //the current coin is more than we need, try next denom @@ -1008,14 +1035,15 @@ export function selectCoinForOperation( // we made it return result; } - if (op === "credit") { + if (op === OperationType.Credit) { //doing withdraw there is no way to cover the gap return result; } //tried all the coins but there is a gap //doing deposit we can try refreshing coins - const total = mode === "gross" ? result.totalValue : result.totalContribution; + const total = + mode === AmountMode.Gross ? result.totalValue : result.totalContribution; const gap = Amounts.sub(limit, total).amount; //about recursive calls @@ -1027,7 +1055,7 @@ export function selectCoinForOperation( refreshIteration: while (refreshIdx < coins.list.length) { const d = coins.list[refreshIdx]; const denomContribution = - mode === "gross" + mode === AmountMode.Gross ? Amounts.sub(d.value, d.denomRefresh).amount : Amounts.sub(d.value, d.denomDeposit, d.denomRefresh).amount; @@ -1038,7 +1066,7 @@ export function selectCoinForOperation( } const changeCost = selectCoinForOperation( - "credit", + OperationType.Credit, changeAfterDeposit, mode, coins, @@ -1067,7 +1095,7 @@ export function selectCoinForOperation( refreshIdx++; } if (choice) { - if (mode === "gross") { + if (mode === AmountMode.Gross) { result.totalValue = Amounts.add(result.totalValue, gap).amount; result.totalContribution = Amounts.add( result.totalContribution, @@ -1096,9 +1124,9 @@ export function selectCoinForOperation( } type CompareCoinsFunction = (d1: CoinInfo, d2: CoinInfo) => -1 | 0 | 1; -function buildRankingForCoins(op: "debit" | "credit"): CompareCoinsFunction { +function buildRankingForCoins(op: OperationType): CompareCoinsFunction { function getFee(d: CoinInfo) { - return op === "credit" ? d.denomWithdraw : d.denomDeposit; + return op === OperationType.Credit ? d.denomWithdraw : d.denomDeposit; } //different exchanges may have different wireFee //ranking should take the relative contribution in the exchange @@ -1116,28 +1144,32 @@ function buildRankingForCoins(op: "debit" | "credit"): CompareCoinsFunction { }; } -function getOperationType(txType: TransactionType): "credit" | "debit" { +function getOperationType(txType: TransactionType): OperationType { const operationType = txType === TransactionType.Withdrawal - ? ("credit" as const) + ? OperationType.Credit : txType === TransactionType.Deposit - ? ("debit" as const) + ? OperationType.Debit : undefined; if (!operationType) { - throw Error(`operation type ${txType} not supported`); + throw Error(`operation type ${txType} not yet supported`); } return operationType; } function getAmountsWithFee( - op: "debit" | "credit", + op: OperationType, value: AmountJson, contribution: AmountJson, details: any, ): GetPlanForOperationResponse { return { - rawAmount: Amounts.stringify(op === "credit" ? value : contribution), - effectiveAmount: Amounts.stringify(op === "credit" ? contribution : value), + rawAmount: Amounts.stringify( + op === OperationType.Credit ? value : contribution, + ), + effectiveAmount: Amounts.stringify( + op === OperationType.Credit ? contribution : value, + ), details, }; } @@ -1202,7 +1234,7 @@ interface CoinsFilter { */ async function getAvailableCoins( ws: InternalWalletState, - op: "credit" | "debit", + op: OperationType, currency: string, filters: CoinsFilter = {}, ): Promise<AvailableCoins> { @@ -1286,7 +1318,7 @@ async function getAvailableCoins( let creditDeadline = AbsoluteTime.never(); let debitDeadline = AbsoluteTime.never(); //4.- filter coins restricted by age - if (op === "credit") { + if (op === OperationType.Credit) { const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll( exchangeBaseUrl, ); diff --git a/packages/taler-wallet-core/src/util/denominations.ts b/packages/taler-wallet-core/src/util/denominations.ts index 2b2faa458..76716cf7a 100644 --- a/packages/taler-wallet-core/src/util/denominations.ts +++ b/packages/taler-wallet-core/src/util/denominations.ts @@ -458,7 +458,9 @@ export function isWithdrawableDenom( ): boolean { const now = AbsoluteTime.now(); const start = AbsoluteTime.fromProtocolTimestamp(d.stampStart); - const withdrawExpire = AbsoluteTime.fromProtocolTimestamp(d.stampExpireWithdraw); + const withdrawExpire = AbsoluteTime.fromProtocolTimestamp( + d.stampExpireWithdraw, + ); const started = AbsoluteTime.cmp(now, start) >= 0; let lastPossibleWithdraw: AbsoluteTime; if (denomselAllowLate) { |